为什么无法进行部分类模板参数推导?

33
我的理解是,类模板的模板参数推导 提案的目的是在推导上下文中使模板函数和模板类的行为更加一致。但我认为我可能误解了一些东西。
如果我们有这个模板对象:
template <std::size_t S, typename T>
struct test
{
    static constexpr auto size = S;
    using type_t = T;

    test(type_t (&input)[size]) : data(input) {}
    type_t (&data)[size]{};
};

我倾向于使用帮助函数作为创建test对象的语法糖:

template <std::size_t S, typename T>
test<S, T> helper(T (&input)[S]) { return input; }

以下是可以使用的示例:
int main()
{
    int buffer[5];

    auto a = helper<5, int>(buffer); // No deduction
    auto b = helper<5>(buffer);      // Type deduced
    auto c = helper(buffer);         // Type and size deduced

    std::cout << a.size << b.size << c.size;

    return 0;
}

上面的代码按预期输出555。我在使用更新的编译器设置1在Wandbox中尝试了相同的操作:

int main()
{
    int buffer[5];

    test<5, int> a(buffer); // No deduction: Ok.
    test<5> b(buffer);      // Type deduced: FAILS.
    test c(buffer);         // Type and size deduced: Ok.

    std::cout << a.size << b.size << c.size;

    return 0;
}

看起来对于类模板的模板参数推导只能推导所有参数,我原本期望这两种行为(辅助函数和类模板)是相同的,我理解错了什么吗?

1Wandbox中可用的最新编译器版本为gcc HEAD 7.0.1 201701和clang HEAD 5.0.0 (trunk)。


2
type_t (&data)[size]{}; 是一个数组引用吗? {} 是初始化器吗?这段代码能编译通过吗?另外,“语法糖”听起来相当调皮。 :) - wally
2
请注意,添加显式的推断指南是没有帮助的。我认为部分推断不被支持,因为标准将推断占位符定义为模板名称(即没有<...>语法)。因此,test<5>不是有效的推断占位符。 - Vittorio Romeo
@Muscampester type_t (&data)[size]{}; 是一个数组引用,没错。{} 确实是初始化器,并且可以编译通过 试一下吧!。至于 语法糖,我能说什么呢……:'( 英语不是我的母语,我经常犯错误! - PaperBirdMaster
据我所记,像 tuple<int> t{5, nullptr}; 这样的东西被认为是不可取的。 - chris
2个回答

26

来自Botond Ballo的优秀旅行报告

The feature as originally proposed included a provision for partial deduction, where you explicitly specify some of the template arguments, and leave the rest to be deduced, but this was pulled over concerns that it can be very confusing in some cases:

// Would have deduced tuple<int, string, float>,
// but tuple<int> is a well-formed type in and of itself!
tuple<int> t(42, "waldo", 2.0f);

8
我觉得这种推理非常奇怪。首先,我们一直认为std::make_tuple<int> (42, "waldo", 2.0f);所做的事情是错误的,并教导人们在参数已被推断时指定模板参数是错误的。现在通过禁止这样做,我们使类模板的推断方式与函数模板不同(不一致),并阻止了大量有效用例(不同于牵强的元组示例)。 - Predelnik
5
问题在于现有代码可能使用带有默认模板参数的 A<T,U> a(t,u,v)(或长度为2而不是3的参数包)。你不能擅自改变依赖于完全可靠的不推导的含义。也许如果只使用显式的推导指南,那就没问题了,但隐式推导则太危险了。 - Davis Herring
2
@DavisHerring 是的,破坏现有代码似乎是一个更好的理由,但不能将其应用于合理的用例仍然很遗憾。 - Predelnik
1
可以考虑添加占位符语法 tuple<int, _, _> t(42, "waldo", 2.0f); - Norbrecht

18
这里似乎存在矛盾。查看P0091R3,似乎明确允许部分指定参数:

我们建议在两种情况下允许将模板名称作为简单类型说明符或具有部分提供的显式模板参数引用类模板:

但是,同一提案中的实际标准措辞并没有提供处理“部分提供的显式模板参数”的方法。不允许将template-name作为简单类型说明符来引用带有模板参数。

因此,遵循规范本身,编译器的行为似乎是正确的。


12
是的,特别是那篇论文作者在修改措辞和设计时没有保持介绍部分的同步,这是一个相当严重的案例。不幸的是,结果是一个会误导人的介绍部分。 - T.C.
很好,他们在进行最终投票之前还有另一次会议。 - Mikel F
2
@MikelF:你假设他们实际上会允许部分规范。文本中的不一致表明这是委员会决定删除的内容,而不是原作者在措辞上犯了错误。 - Nicol Bolas
@NicoBolas 我实际上认为(或者至少希望)他们会使其保持一致。 - Mikel F
至少,通知作者当前的实现方式会破坏一些东西可能是值得的。 - Mikel F

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