g++未采用可变参数模板的推导指南,clang++采用了 - 谁是正确的?

17

考虑以下代码:

template <typename... Types>
struct list
{
    template <typename... Args>
    list(Args...) 
    {
        static_assert(sizeof...(Types) > 0);
    }
};

template <typename... Args>
list(Args...) -> list<Args...>;

int main()
{
    list l{0, 0.1, 'a'};
}

我期望decltype(l)list<int,double,char>。不幸的是,g++ 7.2g++ trunk未通过静态断言测试。clang++ 5.0.0clang++ trunk编译并按预期工作。

godbolt.org conformance view

这是g++的一个错误吗? 还是这里有一个不应该遵循推导指南的原因?


在构造函数上添加SFINAE约束似乎提供了所需的行为:

template <typename... Args, 
          typename = std::enable_if_t<sizeof...(Args) == sizeof...(Types)>>
list(Args...) 
{
    static_assert(sizeof...(Types) > 0);
}

godbolt.org 符合性视图


我认为你的代码可能存在问题... 对于 sizeof...(Types) == 0,你的模板构造函数没有可行的实例化... - W.F.
我很难将其视为除了gcc错误之外的任何其他问题。使用先前的make_list()函数方法可以正常工作。 https://godbolt.org/g/vag3rD - Richard Hodges
嗯,在进行重载决议时,还有一个假设的函数template<typename... Types, typename... Args> list<Types...> deducer(Args...); - 它比推导指南的template<typename.... Args> list<Args...> deducer(Args...);更加专业化,不是吗? - aschepler
不,我猜两者都不是更加专业化的... - aschepler
为什么编译器会尝试实例化list<>?这并没有指示。 - Richard Hodges
显示剩余3条评论
1个回答

13

这是gcc错误80871。问题在于,我们得到了这个推导候选集:

template <class... Types, class... Args>
list<Types...> __f(Args... ); // constructor

template <class... Args>
list<Args...>  __f(Args... ); // deduction-guide

两者都是有效的(在第一种情况下,Types... 可以推断为空),但是在这里调用应该是模棱两可的 - 没有一种比另一种更加专业化。 Types... 在此处不参与排序(类似于[temp.deduct.partial]/12中的示例)。因此,正确的行为是继续到下一个附加规则, 优先选择推导指南[over.match.best]/1.10。因此,应该是 list<int, double, char>

然而,gcc 的行为是优先考虑构造函数,因此static_assert被触发,因为在那种情况下Types...确实为空。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接