为什么Visual Studio无法在模板类中选择正确的构造函数?

Naz*_*kyi 6 c++ templates template-templates type-deduction

当我实例化模板类时,Visual Studio没有看到正确的构造函数。我在哪里弄错了?

我已经尝试过使复制/移动构造函数显式/删除。无济于事。

#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;
}
Run Code Online (Sandbox Code Playgroud)

错误:

error C2664:  'connection<T>::connection(connection<T> &&)': cannot convert argument 1 from 'node<int, connection> *' to 'node<T, connection<T>> *'
Run Code Online (Sandbox Code Playgroud)

son*_*yao 6

似乎是VS的错误。VS似乎将注入的类名 connection视为与等效的类型名connection<T>,但应将其视为类模板本身的模板名,即connectionin node<T, connection>* n;和中connection(node<T, connection>* n),因为的第二个模板参数node是模板模板参数。

(强调我的)

在以下情况下,injected-class-name被视为类模板本身的模板名称:

  • 它后面跟着<
  • 它用作与模板模板参数相对应的模板参数
  • 它是朋友类模板声明的详细类说明符中的最终标识符。

否则,将其视为类型名称,并且等效于模板名称,后跟<>中包含的类模板的模板参数。

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>
};
Run Code Online (Sandbox Code Playgroud)

PS:您的代码可以使用clang很好地编译。

PS:视为connection<T>bool operator<(const connection& b) const


asc*_*ler 3

在类模板的范围内,模板的名称实际上是“注入的类名称”,其作用类似于类成员,并且可以用作模板名称或类型名称,这意味着使用中的专业化。([本地温度]/1

因此,当这个名称用作模板参数时,它可能意味着其中之一,因此编译器需要检查相应的模板参数是类型还是模板。g++ 和 clang++ 按原样接受您的代码。但是 MSVC 有一个错误,它经常(但并非总是)假设用作模板参数的注入类名是类类型,即使唯一相关的模板参数是模板模板参数。(原代码上的三个编译器: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;
    }
};
Run Code Online (Sandbox Code Playgroud)

(所有三个编译器都接受这一点: https: //godbolt.org/z/st7liP