扣除指南和可变参数模板

Vin*_*ent 9 c++ tuples g++ template-argument-deduction c++17

请考虑以下代码:

#include <tuple>
#include <iostream>

template <class T>
struct custom_wrapper
{
    template <class Arg>
    custom_wrapper(Arg arg): data(arg) {}
    T data;
};

template <class Arg>
custom_wrapper(Arg arg) -> custom_wrapper<Arg>;

template <class... T>
struct custom_tuple
{
    template <class... Args>
    custom_tuple(Args... args): data(args...) {}
    std::tuple<T...> data;
};

template <class... Args>
custom_tuple(Args... args) -> custom_tuple<Args...>;

int main(int argc, char* argv[])
{
    custom_wrapper<int> w1(42);  // OK
    custom_wrapper w2(42);       // OK
    custom_tuple<int> t1(42);    // OK
    custom_tuple t2(42);         // Fails
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

失败的行在g ++ 7下返回以下错误:

variadic_deduction_guide.cpp: In instantiation of 'custom_tuple<T>::custom_tuple(Args ...) [with Args = {int}; T = {}]':
variadic_deduction_guide.cpp:31:23:   required from here
variadic_deduction_guide.cpp:19:45: error: no matching function for call to 'std::tuple<>::tuple(int&)'
     custom_tuple(Args... args): data(args...) {}
Run Code Online (Sandbox Code Playgroud)

这是正常的还是编译器错误?

Bar*_*rry 4

这是gcc 错误 80871。接下来是对为什么代码格式良好的解释(并且 clang 正确地判定它t2是 a custom_tuple<int>)。


弄清楚要做什么的过程

custom_tuple t2(42);
Run Code Online (Sandbox Code Playgroud)

基本上涉及合成一堆函数并对它们执行重载解析。相关候选者是来自一个构造函数和推导指南的综合函数:

template <class... T, class... Args>
custom_tuple<T...> foo(Args... );     // the constructor

template <class... Args>
custom_tuple<Args...> foo(Args... );  // the deduction guide
Run Code Online (Sandbox Code Playgroud)

从这一点开始,您可以根据[temp.arg.explicit]/3对“尾随参数包”的解释来选择您自己的冒险:

未以其他方式推导的尾随模板参数包将被推导为模板参数的空序列。如果所有模板实参都可以推导,则可以全部省略;在这种情况下,空模板参数列表<>本身也可以被省略。

T...不落后

这个案子很简单。我们只有一个可行的候选者(因为T...不可推导)——演绎指南候选者。我们推论Args...{int},所以我们最终得到custom_tuple<int>

T...正在落后

gcc 和 clang 实际上都考虑了构造函数的成功演绎。所以我们进入[over.match.best]的决胜局:

给定这些定义,一个可行的函数F1被定义为比另一个可行的函数更好的F2函数,如果 [...]

  • F1F2是函数模板特化,并且根据 [temp.func.order] 中描述的部分排序规则, 函数F1模板比​​模板更特化,或者,如果不是这样,F2
  • F1是从演绎指南([over.match.class.deduct])生成的,但F2不是,或者,如果不是,[...]

出于部分排序的目的,相关类型只是与函数参数相对应的类型,并且我们可以忽略未使用的模板参数,因此两个函数模板都不被认为比另一个更专业。

这让我们更喜欢演绎指南,这是整个过程中最简单的步骤。我们推论Args...{int},所以我们最终得到custom_tuple<int>


无论哪种方式,custom_tuple<int>都是正确的决定。