admin管理员组文章数量:1321043
I found a strange behavior with a custom type trait I was writing and further found that different compilers are having different behavior with the trait. The goal is simple, I want to detect if a given function MyFunction
is defined/overloaded for a specific input type. I made a trait HasMyFunction
to do this. The example code and godbolt link for 3 compilers behavior is below.
To me the clang and gcc behavior is surprising/incorrect. The trait returns false for primitive types and even std::string
unless MyFunction
for those types is defined PRIOR to the template definition for the trait. However if I use a locally defined/custom type the same trait always returns true, no matter if the class and its MyFunction
is defined before or after the trait.
MSVC returns true in all cases regardless of the ordering.
My understanding is that templates don't "exist" until they are instantiated and given that the instantiation in all cases is well after all these other declarations I don't see why this ordering would matter.
What is the correct behavior here, and which of the compilers is non compliant? This does not seem like the type of thing that would be compiler dependent or left to the implementers....
Godbolt
#include <iostream>
#include <type_traits>
#include <string>
/// Helper to determine if a type has a specialization of the MyFunction function
template <typename T, typename = void>
struct HasMyFunction : public std::false_type{};
template <typename T>
struct HasMyFunction<T, std::void_t<decltype(MyFunction(std::declval<T>()))>> : public std::true_type{};
//Trait works no matter the location of this def
struct Test{};
std::string MyFunction(Test)
{
return "";
}
struct TestBad{};
//Move me up above line 5 and the trait works as you would expect (true)
//MSVC seems to work in all cases
//gcc/clang gives false unless this is declared before the template....
std::string MyFunction(int)
{
return "";
}
//Same behavior with std::string (non primative type....)
std::string MyFunction(std::string)
{
return "";
}
int main() {
std::cout << HasMyFunction<int>::value << std::endl;
std::cout << HasMyFunction<std::string>::value << std::endl;
std::cout << HasMyFunction<Test>::value << std::endl;
std::cout << HasMyFunction<TestBad>::value << std::endl;
}
I found a strange behavior with a custom type trait I was writing and further found that different compilers are having different behavior with the trait. The goal is simple, I want to detect if a given function MyFunction
is defined/overloaded for a specific input type. I made a trait HasMyFunction
to do this. The example code and godbolt link for 3 compilers behavior is below.
To me the clang and gcc behavior is surprising/incorrect. The trait returns false for primitive types and even std::string
unless MyFunction
for those types is defined PRIOR to the template definition for the trait. However if I use a locally defined/custom type the same trait always returns true, no matter if the class and its MyFunction
is defined before or after the trait.
MSVC returns true in all cases regardless of the ordering.
My understanding is that templates don't "exist" until they are instantiated and given that the instantiation in all cases is well after all these other declarations I don't see why this ordering would matter.
What is the correct behavior here, and which of the compilers is non compliant? This does not seem like the type of thing that would be compiler dependent or left to the implementers....
Godbolt
#include <iostream>
#include <type_traits>
#include <string>
/// Helper to determine if a type has a specialization of the MyFunction function
template <typename T, typename = void>
struct HasMyFunction : public std::false_type{};
template <typename T>
struct HasMyFunction<T, std::void_t<decltype(MyFunction(std::declval<T>()))>> : public std::true_type{};
//Trait works no matter the location of this def
struct Test{};
std::string MyFunction(Test)
{
return "";
}
struct TestBad{};
//Move me up above line 5 and the trait works as you would expect (true)
//MSVC seems to work in all cases
//gcc/clang gives false unless this is declared before the template....
std::string MyFunction(int)
{
return "";
}
//Same behavior with std::string (non primative type....)
std::string MyFunction(std::string)
{
return "";
}
int main() {
std::cout << HasMyFunction<int>::value << std::endl;
std::cout << HasMyFunction<std::string>::value << std::endl;
std::cout << HasMyFunction<Test>::value << std::endl;
std::cout << HasMyFunction<TestBad>::value << std::endl;
}
Share
Improve this question
edited Jan 17 at 19:38
I Less3 CPP
asked Jan 17 at 19:33
I Less3 CPPI Less3 CPP
1215 bronze badges
5
|
1 Answer
Reset to default 2@kjpus got me on the path to finding this with the specific mention of dependent name, but he didn't submit in the form of an answer so I am putting the answer here explicitly.
So this is due to dependent name resolution and is explained quite clearly on cppref with an example well aligned with my original example code.
Non-dependent names are looked up and bound at the point of template definition. This binding holds even if at the point of template instantiation there is a better match:
#include <iostream>
void g(double) { std::cout << "g(double)\n"; }
template<class T>
struct S
{
void f() const
{
g(1); // "g" is a non-dependent name, bound now
}
};
void g(int) { std::cout << "g(int)\n"; }
int main()
{
g(1); // calls g(int)
S<int> s;
s.f(); // calls g(double)
}
本文标签: gccC Type traits dependent upon declaration ordering and varying compiler behaviorStack Overflow
版权声明:本文标题:gcc - C++ Type traits dependent upon declaration ordering and varying compiler behavior - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742094383a2420458.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
/permissive-
compiler option, but reverts to what you observed if you also add/Zc:twoPhase-
. This means the lack of 2-phase lookup in MSVC by default is the reason why it differs from the other compilers. But I do not know why this behavior is correct. – Eugene Commented Jan 17 at 21:45