为模板参数提供默认参数时,VS 2017无法正确找到先前定义的类型

时间:2018-08-01 11:56:46

标签: c++ templates gcc visual-studio-2017

以下代码:

namespace N {
    template <typename A, typename B = A>
    class Foo;
}

template <typename C, typename D>
class N::Foo {
public:
    Foo() = default;
};

int main()
{
    N::Foo<char> foo;
}

在名称空间Foo中涉及类模板N的(转发)声明 具有两个类型参数(AB)。 (向前)声明为第二个参数提供了一个与第一个参数(A)相同的默认值。稍后,使用不同名称的参数(CD)定义此类。由于已经指定,因此未提供默认值。

这会在GCC 7.1和Clang 3.8(在C ++ 17模式下)中进行编译,只是会警告未使用的变量foo。但是,VS 2017 15.7.5(在C ++ 17模式下)说A是未定义的标识符,并且说它不是参数D的有效类型名称。

令我惊讶的是,当我将代码更改为此:

namespace N {
    template <typename A, typename B = C> //note: A changed into C
    class Foo;
}

template <typename C, typename D>
class N::Foo {
public:
    Foo() = default;
};

int main()
{
    N::Foo<char> foo;
}

VS接受它! (不过,它不能在GCC或Clang中编译)

我认为VS在这里是错误的。它尝试在不同的上下文中解释默认参数(在这种特定情况下,Foo的定义),而GCC和Clang则不然。我在哪一步错了?还是这是VS的错误?

任何帮助将不胜感激。

(对不起,我的英语不好)

2 个答案:

答案 0 :(得分:2)

我会说这绝对是编译器错误。在后继参数的默认参数中使用前一个模板参数的名称是常见的做法,标准库本身会在所有位置执行此操作(例如,标准容器的分配器和比较函数)。该标准甚至在[basic.scope.temp] §3的注释中明确提到了这种可能性。

VS 2015似乎也受到了影响。请注意,模板甚至不必位于另一个名称空间中。我进一步简化了您的示例:

template <typename A, typename B = A>
struct S;

template <typename C, typename D>
struct S {};

int main()
{
    S<int> s;
}

这似乎已经触发了问题……

答案 1 :(得分:-1)

尝试一下。

namespace N {
    template <typename A, typename B = A>
    class Foo;
}

template <typename A, typename B> //A and B is TYPE
class N::Foo {
public:
    Foo() = default;
};

int main()
{
    N::Foo<char> foo;
}

您可以在网页中查看原因:https://docs.microsoft.com/en-us/cpp/cpp/templates-cpp

  

T是模板参数; typename关键字表示   参数是类型的占位符。调用该函数时,   编译器将 T的每个实例替换为具体类型   由用户指定或由推断的参数   编译器。

例如,如果您编写如下代码,则必须是错误

double example(double,int);

int main()
{
//...
}

double example(double a,double b)
{
//...
}

因为您指定的TYPE与原型冲突。

(对不起我的英语不好)