我试图通过将Visual Studio的输出与GCC的输出进行比较来调试我在Visual Studio中遇到的问题,但我似乎无法获得极其简化的版本要在Ideone中编译的代码:
#include <exception>
#include <iostream>
template <int x>
struct foo {};
template <>
struct foo<0> { using type = int; };
template <typename Foo>
struct bar
{
bar(void) {}
template <typename = typename Foo::type>
bar(const bar &) {}
};
int main(void)
{
bar<foo<1>> x;
}
当我尝试使用Ideone运行它时,上面给出了错误:
prog.cpp: In instantiation of ‘struct bar<foo<1> >’:
prog.cpp:21:17: required from here
prog.cpp:16:5: error: no type named ‘type’ in ‘struct foo<1>’
bar(const bar &) {}
根据我的理解,替换Foo::type
的失败不应导致编译错误。请注意,使复制构造函数bar(const bar &) { typename Foo::type x; }
允许程序编译得很好。我尝试使用此模板的方式与使用std::enable_if
的方式非常相似,这在类中的任何位置使用时也会导致编译错误 - 而不仅仅是复制构造函数。这是正确的行为吗?我相当肯定它不是,但我一直认为Ideone在后端使用GCC ......
答案 0 :(得分:4)
是的,这种行为是正确的。
当在函数模板重载决策期间推导模板参数时,SFINAE适用(允许编译器丢弃特殊化而不发出错误)。
在您的情况下,错误在重载解析期间没有发生,但在模板实例化期间:bar
模板根本无法实例化,因为复制构造函数需要存在嵌套类型Foo::type
,当模板参数为foo<1>
时不存在。
关于你的最后一点,如果你宣布:
template <typename Foo>
struct bar
{
bar(void) {}
bar(const bar &) { typename Foo::type x; }
};
只要不调用复制构造函数,它就可以编译。 使用复制构造函数将导致相同的编译错误:
bar<foo<1>> x2 = x; // Error