具有模板参数推导和默认模板参数的模板变量

Pap*_*ter 8 c++ templates language-lawyer default-parameters c++17

通过一个类似的问题感到惊讶(和共同),我自己尝试了标准中提到的问题的例子:

template <typename T, typename U = int> struct S;
template <typename T = int, typename U> struct S
{ void f() { std::cout << __PRETTY_FUNCTION__ << '\n'; } };

int main()
{
    S s; s.f();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

上面的代码打印 void S<int, int>::f() [T = int, U = int] 使用gcc HEAD 8.0.1 201803编译,但无法使用clang HEAD 7.0.0进行编译,除非在实例化期间使用尖括号:

S s; s.f(); // error: declaration of variable 's' with deduced type 'S' requires an initializer
S<> t; t.f(); // Correct
Run Code Online (Sandbox Code Playgroud)

抛开这个问题,我已经检查了这个特定行为的其他模板风格,并以非常不规则的方式接受或拒绝代码:

Template function
template <typename T, typename U = int> void function();
template <typename T = int, typename U> void function()
{ std::cout << __PRETTY_FUNCTION__ << '\n'; }

int main()
{
    /* Rejected by GCC: no matching function for call to 'function()'
       template argument deduction/substitution failed:
       couldn't deduce template parameter 'T'
       same error with function<>()

       CLang compiles without issues */
    function(); // CLang prints 'void function() [T = int, U = int]'
    return 0;
}
Run Code Online (Sandbox Code Playgroud) Template variable
template <typename T, typename U = int> int variable;
template <typename T = int, typename U> int variable = 0;

int main()
{
    /* GCC complains about wrong number of template arguments (0, should be at least 1)
     while CLang complains about redefinition of 'variable' */
    std::cout << variable<> << '\n';
    return 0;
}
Run Code Online (Sandbox Code Playgroud) Template alias
template <typename T, typename U = int> using alias = int;
template <typename T = int, typename U> using alias = int;

int main()
{
    /* GCC complains about redefinition of 'alias'
       while CLang compiles just fine. */
    alias<> v = 0;
    std::cout << v << '\n';
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

关于此功能的标准文本并未区分不同的模板类型,因此我认为它们的行为应该相同.

但是,模板变量的情况是两个编译器都拒绝的情况,所以我对模板变量选项有一些疑问.我认为CLang正确拒绝模板变量抱怨重新定义而GCC因错误原因拒绝代码是错误的,这是有道理的,但这种推理并不符合标准在[temp.param]/10中所说的.

那么对于模板变量的情况我应该期待什么?:

  • 代码因重新定义而被拒绝(CLang是对的).
  • 代码被接受,合并两个模板定义(GCC和CLang都错了).

Pet*_*r K 1

免责声明:以下内容在 C++14 上下文中有效。对于 C++17,两个编译器都是错误的。请参阅巴里的另一个答案。

仔细查看细节,我发现 Clang 在这里是正确的,而 GCC 则很混乱。

  • 第一种情况,类模板(与函数模板不同)确实需要<>.

  • 第二种情况(函数模板)由 Clang 处理,与第一种情况完全相同,没有语法要求 use<>来指示使用模板。这在 C++ 中适用于所有上下文中的函数模板。

  • 第三种情况:就变量而言,我发现 Clang 是正确的,而 GCC 很混乱。如果您使用 Clang 重新声明变量,extern则接受它。

    template <typename T, typename U = int> int variable = 0;
    template <typename T = int, typename U> extern int variable;
    
    int main()
    {
        // accepted by clang++-3.9 -std=c++14
        std::cout << variable<> << '\n';
        return 0;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    因此它的行为再次与标准情况和之前的情况一致。没有extern这个就是重新定义并且是被禁止的。

  • 第四种情况,using模板。Clang 再次表现一致。我使用 typeid 来确保别名确实是 int:

    template <typename T, typename U = int> using alias = int;
    template <typename T = int, typename U> using alias = int;
    
    int main()
    {
        alias<> v = 0;
        std::cout << v << '\n';
        std::cout << typeid(v).name() << '\n';
        return 0;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    然后

    $ ./a.out | c++filt -t
    
    Run Code Online (Sandbox Code Playgroud)

    输出

    0
    int
    
    Run Code Online (Sandbox Code Playgroud)

因此,正如标准中所述,Clang 确实对重新声明哪种模板没有区别。

  • *“第一种情况,类模板(与函数模板不同)确实需要 &lt;&gt;”* 在 C++17 中不再如此。 (2认同)