为什么 Visual Studio 无法在模板 class 中选择正确的构造函数?
Why does Visual Studio fail to choose the right constructor in template class?
当我实例化模板 class 时,Visual Studio 没有看到正确的构造函数。我哪里弄错了?
我已经尝试制作 copy/move 个构造函数 explicit/deleted。没用。
#include <set>
using namespace std;
template <class T, template<class> class ConnectionType>
struct node
{
T value;
node(const T& value) : value(value) {}
set<ConnectionType<T>> connections;
};
template <class T>
struct connection
{
node<T, connection>* n;
connection(node<T, connection>* n) :
n(n) {}
bool operator<(const connection& b) const
{
return n < b.n;
}
};
int main()
{
node<int, connection> a(0);
connection<int> c(&a); // ERROR HERE
return 0;
}
错误:
error C2664: 'connection<T>::connection(connection<T> &&)': cannot convert argument 1 from 'node<int, connection> *' to 'node<T, connection<T>> *'
好像是VS的bug。 VS 似乎将 injected class name connection
视为等同于 connection<T>
的类型名称,但应将其视为 class 模板本身的模板名称,即 [= node<T, connection>* n;
和connection(node<T, connection>* n)
中的11=],因为node
的第二个模板参数是模板模板参数。
(强调我的)
In the following cases, the injected-class-name is treated as a
template-name of the class template itself:
- it is followed by <
- it is used as a template argument that corresponds to a template template parameter
- it is the final identifier in the elaborated class specifier of a friend class template declaration.
Otherwise, it is treated as a type-name, and is equivalent to the
template-name followed by the template-parameters of the class
template enclosed in <>.
template <template <class, class> class> struct A;
template<class T1, class T2>
struct X {
X<T1, T2>* p; // OK, X is treated as a template-name
using a = A<X>; // OK, X is treated as a template-name
template<class U1, class U2>
friend class X; // OK, X is treated as a template-name
X* q; // OK, X is treated as a type-name, equivalent to X<T1, T2>
};
PS:您的代码在 clang 下编译得很好。
PS:在bool operator<(const connection& b) const
中被当作connection<T>
处理。
在 class 模板的范围内,模板的名称实际上是 "injected class name",它的作用类似于 class 成员,可以用作模板名称或类型名称,表示使用的专业化。 ([temp.local]/1)
所以当这个名字被用作模板参数时,它可能意味着任何一个,因此编译器需要检查相应的模板参数是类型还是模板。 g++ 和 clang++ 按原样接受您的代码。但是 MSVC 有一个错误,它经常(但不总是)假定用作模板参数的注入 class 名称是 class 类型,即使唯一相关的模板参数是模板模板参数也是如此。 (原代码上的三个编译器:https://godbolt.org/z/xrJSPB)
要解决此问题,当您指的是模板自身范围内的模板名称时,您可以使用限定名称,例如 ::connection
。
template <class T>
struct connection
{
node<T, ::connection>* n;
connection(node<T, ::connection>* n) :
n(n) {}
bool operator<(const connection& b) const
{
return n < b.n;
}
};
(所有三个编译器都接受这个:https://godbolt.org/z/st7liP)
Visual Studio 没有看到正确的构造函数。我哪里弄错了?
我已经尝试制作 copy/move 个构造函数 explicit/deleted。没用。
#include <set>
using namespace std;
template <class T, template<class> class ConnectionType>
struct node
{
T value;
node(const T& value) : value(value) {}
set<ConnectionType<T>> connections;
};
template <class T>
struct connection
{
node<T, connection>* n;
connection(node<T, connection>* n) :
n(n) {}
bool operator<(const connection& b) const
{
return n < b.n;
}
};
int main()
{
node<int, connection> a(0);
connection<int> c(&a); // ERROR HERE
return 0;
}
错误:
error C2664: 'connection<T>::connection(connection<T> &&)': cannot convert argument 1 from 'node<int, connection> *' to 'node<T, connection<T>> *'
好像是VS的bug。 VS 似乎将 injected class name connection
视为等同于 connection<T>
的类型名称,但应将其视为 class 模板本身的模板名称,即 [= node<T, connection>* n;
和connection(node<T, connection>* n)
中的11=],因为node
的第二个模板参数是模板模板参数。
(强调我的)
In the following cases, the injected-class-name is treated as a template-name of the class template itself:
- it is followed by <
- it is used as a template argument that corresponds to a template template parameter
- it is the final identifier in the elaborated class specifier of a friend class template declaration.
Otherwise, it is treated as a type-name, and is equivalent to the template-name followed by the template-parameters of the class template enclosed in <>.
template <template <class, class> class> struct A; template<class T1, class T2> struct X { X<T1, T2>* p; // OK, X is treated as a template-name using a = A<X>; // OK, X is treated as a template-name template<class U1, class U2> friend class X; // OK, X is treated as a template-name X* q; // OK, X is treated as a type-name, equivalent to X<T1, T2> };
PS:您的代码在 clang 下编译得很好。
PS:在bool operator<(const connection& b) const
中被当作connection<T>
处理。
在 class 模板的范围内,模板的名称实际上是 "injected class name",它的作用类似于 class 成员,可以用作模板名称或类型名称,表示使用的专业化。 ([temp.local]/1)
所以当这个名字被用作模板参数时,它可能意味着任何一个,因此编译器需要检查相应的模板参数是类型还是模板。 g++ 和 clang++ 按原样接受您的代码。但是 MSVC 有一个错误,它经常(但不总是)假定用作模板参数的注入 class 名称是 class 类型,即使唯一相关的模板参数是模板模板参数也是如此。 (原代码上的三个编译器:https://godbolt.org/z/xrJSPB)
要解决此问题,当您指的是模板自身范围内的模板名称时,您可以使用限定名称,例如 ::connection
。
template <class T>
struct connection
{
node<T, ::connection>* n;
connection(node<T, ::connection>* n) :
n(n) {}
bool operator<(const connection& b) const
{
return n < b.n;
}
};
(所有三个编译器都接受这个:https://godbolt.org/z/st7liP)