【问题标题】:Variadic function wont compile with clang可变参数函数不会用 clang 编译
【发布时间】:2019-03-08 05:16:10
【问题描述】:

首先,如果这是重复的,我很抱歉,我很乐意将其删除,但我什至不确定这里的问题/诊断是什么。

无论如何,我这里的代码适用于 gcc 而不是 clang - 为什么会这样?我显然无法理解为什么 clang 不能编译这个。

#include <iostream>
#include <type_traits>

using std::cout;
using std::endl;


template <typename T, typename... Args, typename std::enable_if<!sizeof...(Args)>::type* = nullptr>
void func(int start_size, int idx)
{
    cout << start_size << " " << idx << endl;
    return;
}

template <typename T, typename... Args, typename std::enable_if<sizeof...(Args)>::type* = nullptr>
void func(int start_size, int idx)
{

    if((idx + 1) == int(start_size - int(sizeof...(Args))))
       {
           cout << start_size << " " << idx << endl;
           return;
       }
       func<Args...>(start_size, idx);
}

template <typename... Args>
void func_wrapper(int idx)
{
    func<Args...>(sizeof...(Args),idx);
}


int main()
{
    func_wrapper<int,double,char>(1);

}

错误:

prog.cc:37:5: error: no matching function for call to 'func'
    func<Args...>(sizeof...(Args),idx);
    ^~~~~~~~~~~~~
prog.cc:44:5: note: in instantiation of function template specialization 'func_wrapper<int, double, char>' requested here
    func_wrapper<int,double,char>(1);
    ^
prog.cc:16:6: note: candidate template ignored: requirement '!sizeof...(Args)' was not satisfied [with T = int, Args = <double, char>]
void func(int start_size, int idx)
     ^
prog.cc:23:6: note: candidate template ignored: substitution failure [with T = int, Args = <double, char>]: non-type template argument evaluates to 2, which cannot be narrowed to type 'bool'
void func(int start_size, int idx)
     ^
1 error generated.

魔杖盒:https://wandbox.org/permlink/yqki47uYcwUlE013

【问题讨论】:

    标签: c++ c++11 templates language-lawyer


    【解决方案1】:

    clang 拒绝此代码是正确的。要消除错误,您应该显式应用转换:

    std::enable_if<bool(sizeof...(Args))>
    

    理由是:

    [temp.arg.nontype]/5

    对每个用作 非类型模板参数。如果非类型模板参数不能 转换为相应模板参数的类型,然后 程序格式不正确。

    (5.1) 对于整数或枚举的非类型模板参数 类型,转换后的常量表达式中允许的转换 ([expr.const]) 被应用

    然后[expr.const]/3 表示不会自动考虑缩小转换:

    类型 T 的转换后的常量表达式是文字常量 表达式,隐式转换为类型 T,其中隐式 在文字常量表达式中允许转换(如果有),并且 隐式转换序列仅包含用户定义的 转换,左值到右值的转换 ([conv.lval]),积分 促销 ([conv.prom]) 和 积分转换 ([conv.integral]) 除了缩小转化率 ([dcl.init.list])

    因此对于这种情况,从std::size_tbool 的缩小转换应该是显式的:bool(sizeof...(Args))

    【讨论】:

    • 感谢您的回答!你怎么知道在标准中的确切位置?我一直希望能够做到这一点......
    • @PYA 你可以先查看目录。相关部分通常可以从目录中看到。 timsong-cpp.github.io/cppwp/n3337/#temp
    • 更好:enable_if&lt;sizeof...(Args)!=0&gt;
    • @PYA 查找标准是您通过尝试和失败获得的技能。继续尝试;)很快就会有一部分你可以感觉到答案是否可以隐藏。
    【解决方案2】:

    除了缺少从std::size_tbool 的显式转换,这需要代码为

    std::enable_if<sizeof...(Args) != 0>
    

    还有一个错误:

    temp#res-8.3

    程序格式错误,不需要诊断,如果:

    [..]

    可变参数模板的每个有效特化都需要一个空模板参数包,

    所以

    template <typename T,
              typename... Args,
              typename std::enable_if<sizeof...(Args) == 0>::type* = nullptr>
    

    无效。
    (还有void* = nullptr 可能有问题)。

    您可以改为使用 2 个重载来编写它:

    template <typename T>
    void func(int start_size, int idx)
    {
        cout << start_size << " " << idx << endl;
    }
    
    template <typename T, typename T2, typename... Args> // Addition of T2, and change sizeof...
    void func(int start_size, int idx)
    {
        if ((idx + 1) == int(start_size - int(1 + sizeof...(Args)))) {
            cout << start_size << " " << idx << endl;
            return;
        }
        func<T2, Args...>(start_size, idx);
    }
    

    【讨论】:

    • 标准中的这种说法令人惊讶。有什么特别的理由要挑出这个案例并将其标记为格式错误吗?我不认为“可变参数模板的每个有效特化都需要一个空模板参数包”是应该区别对待的特殊情况。
    • @liliscent:我不知道背后的原因。警告是有意义的,因为它确实是无用的(但在一般情况下识别起来很复杂)。但是对于需要一种类型的情况,警告也可以(因为我们可能会用单个 typename T 替换它)。
    • 我也找不到对该规则有意义的解释,尽管有几个相关的 SO 问题。顺便说一句,我认为它不适用于这里,因为我们可以专门化 typename T 并保持 typename... Args 不变。这是一个不引入空包的专业化。
    猜你喜欢
    • 2014-12-04
    • 2018-03-25
    • 1970-01-01
    • 1970-01-01
    • 2016-10-05
    • 2021-11-28
    • 1970-01-01
    • 2021-11-26
    相关资源
    最近更新 更多