演绎指南和可变参数模板

考虑以下代码:

#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;
}

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

The line that fails return the following error under 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...) {}

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

Is that normal or is that a compiler bug?

推荐答案

这是 gcc 错误 80871.下面解释了为什么代码格式正确(并且当确定 t2custom_tuple 时,clang 是正确的).

This is gcc bug 80871. What follows is an explanation of why the code is well-formed (and clang is correct in deciding that t2 is a custom_tuple<int>).

弄清楚如何处理的过程

custom_tuple t2(42);

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

basically involves synthesizing a bunch of functions and performing overload resolution on them. The relevant candidates are the synthesized functions from the one constructor and the deduction guide:

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

template <class... Args>
custom_tuple<Args...> foo(Args... );  // the deduction guide

从这一点开始,根据您对 [temp.arg.explicit]/3:

From this point it's a choose your own adventure based on your interpretation of what a "trailing parameter pack" is according to [temp.arg.explicit]/3:

一个尾随模板参数包没有以其他方式推导将被推导为一个空的模板参数序列.如果所有的模板参数都可以推导出来,那么它们都可以被省略;在这种情况下,空模板参数列表 <> 本身也可以省略.

A trailing template parameter pack not otherwise deduced will be deduced to an empty sequence of template arguments. If all of the template arguments can be deduced, they may all be omitted; in this case, the empty template argument list <> itself may also be omitted.

T... 没有尾随

T... isn't trailing

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

This case is easy. We only have one viable candidate (because T... isn't deducible) - the deduction-guide candidate. We deduce Args... as {int}, so we end up with custom_tuple<int>.

T... 尾随

T... is trailing

gcc 和 clang 实际上都认为构造函数的推导成功.所以我们进入 [over.match.best] 中的决胜局:

Both gcc and clang actually do consider deduction to succeed for the constructor. So we go to the tiebreakers in [over.match.best]:

鉴于这些定义,如果 [...]

Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if [...]

  • F1F2 是函数模板特化,F1 的函数模板比?? F2<的模板更特化/code> 根据 [temp.func.order] 中描述的部分排序规则,或者,如果不是这样,
  • F1 是从演绎指南 ([over.match.class.deduct]) 生成的,而 F2 不是,或者,如果不是,[...]
  • F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in [temp.func.order], or, if not that,
  • F1 is generated from a deduction-guide ([over.match.class.deduct]) and F2 is not, or, if not that, [...]

为了部分排序,相关类型只是与函数参数对应的那些,我们可以忽略未使用的模板参数,因此没有一个函数模板被认为比另一个更专业.

For purposes of partial ordering, the relevant types are just those which correspond to function parameters, and we're allowed to ignore unused template parameters, so neither function template is considered more specialized than the other.

这让我们更喜欢演绎指南,这是整个过程中最简单的一步.我们将 Args... 推导出为 {int},所以我们最终得到 custom_tuple.

This leaves us to simply preferring the deduction-guide, which has been the simplest step of this whole process. We deduce Args... as {int}, so we end up with custom_tuple<int>.

无论哪种方式,custom_tuple 都是正确的决定.

Either way, custom_tuple<int> is the correct decision.

相关文章