C++ 类型特征取决于声明顺序和不同的编译器行为

问题描述 投票:0回答:1

我发现我正在编写的自定义类型特征有一个奇怪的行为,并进一步发现不同的编译器对该特征有不同的行为。目标很简单,我想检测是否为特定输入类型定义/重载了给定函数

MyFunction
。我做了一个特质
HasMyFunction
来做到这一点。下面是 3 个编译器行为的示例代码和 godbolt 链接。

对我来说,clang 和 gcc 的行为令人惊讶/不正确。该特征对于原始类型返回 false,甚至返回

std::string
,除非这些类型的
MyFunction
是在该特征的模板定义之前定义的。但是,如果我使用本地定义/自定义类型,则相同的特征始终返回 true,无论类及其
MyFunction
是在特征之前还是之后定义。

无论顺序如何,MSVC 在所有情况下都会返回 true。

我的理解是,模板在实例化之前不会“存在”,并且考虑到所有情况下的实例化都在所有这些其他声明之后,我不明白为什么这个顺序很重要。

这里的正确行为是什么,哪个编译器不合规?这看起来不像是依赖于编译器或留给实现者的事情......

神箭

#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;
}
c++ gcc template-meta-programming type-traits
1个回答
0
投票

@kjpus 让我通过具体提及的依赖名称找到了这个,但他没有以答案的形式提交,所以我将答案明确地放在这里。

因此,这是由于依赖名称解析造成的,并且在 cppref 上进行了非常清楚的解释,其中的示例与我的原始示例代码非常一致。

非依赖名称在模板定义时查找和绑定。即使在模板实例化时存在更好的匹配,此绑定仍然有效:

#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)
}
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.