6

In this example, is it possible to allow the deduction of the template parameters type of the tuple?

#include<tuple>
#include<string>

template<class T1, class T2>
void fun(std::tuple<T1, T2> t, std::string other){}

int main(){
    fun(std::tuple<double, int>(2.,3), std::string("other")); // ok
    fun(std::make_tuple(2.,3), std::string("other")); // ok, but trying to avoid `make_tuple`
    fun({2.,3},std::string("other")); // desired syntax but
    // giving compilation error: candidate template ignored: couldn't infer template argument 'T1' void fun(std::tuple<T1, T2> t)
}

I added the second argument other to avoid solutions involving variadic arguments at the level of the function fun. Also, I am trying to avoid the use of make_tuple, at least from the user code (i.e. in main()). In fact it doesn't need to be the tuple type the one involved as long as the "desired syntax" is allowed and somehow its element types can be deduced at later stage.

(Also, although similar, this has nothing to do with initializer_list since it doesn't work at all having dissimilar elements in the braces)

It fails at least with clang 3.2 and gcc 4.7.2. Is there any hope that it would work with the current or a near-future standard? (e.g. a future(?) initializer_tuple.)

(This can be very useful to add expressiveness to function calls, by aggregating subelements, but that can be argued about)


Note: For the example code it seems that std::forward_as_tuple is more appropriate than std::make_tuple so the arguments are not necessarily copied: http://en.cppreference.com/w/cpp/utility/tuple/forward_as_tuple . Still not as nice as if there were a build-in language feature for heterogeneous initializer lists.

4

2 回答 2

4

不,绝对没有办法。如果元素类型不是同一类型,则扣除失败。如果参数std::initializer_list<T>无论如何都不是,则根本不会进行任何扣除(您是对的,initializer_list与您给出的大括号没有任何关系,但这是扣除工作的简单规则)。

模板参数值必须由涉及它们的其他函数参数位置推导出来,或者必须明确指定。

于 2013-05-23T14:28:07.897 回答
0

现在是 C++20 的时代,仍然没有通用的方法来做到这一点。

解决方法 1:

在括号语法出于任何原因保留给特定参数组合的情况下,有一个部分解决方案:例如

template<class T1 = double, class T2 = int>
void fun(std::tuple<T1, T2> t, std::string other){}

int main(){
    fun({2.,3},std::string("other")); // now works at least, but it may not mean what it seems
}

https://godbolt.org/z/xGaEP5EGn

这个解决方案最糟糕的部分是它允许交换 double 和 int,但我会说这是内置类型的隐式转换的问题。

它适用于任何版本的 C++。

解决方法 2:

自 OP 以来唯一的变化是现在有了 CTAD(类模板参数推导)。

这意味着这个问题有一个新的部分解决方法,即

    fun(std::tuple{2.,3}, std::string("other")); // ok, but still trying to avoid `tuple`

幻想解决方案:

这激发了我的想法,如果将来使用以下语法之一扩展CTAD,则可以做到这一点:

CTAD 的自然扩展是:

void fun(std::tuple t, std::string other){}  // implicitly a template because std::tuple is a template

更明确地说:

void fun(std::tuple<auto, auto> t, std::string other){}  // implicitly a template because std::tuple is a template and `auto`.

或者

template<template<typename...> class Tuple = std::tuple>
void fun(Tuple t, std::string other){}

或者

template<template<typename...> class Tuple = std::tuple, class T1, class T2>
void fun(Tuple<T1, T2> t, std::string other){}

目前这些都不起作用,但我喜欢第一个和最后一个选项。第一个是因为它是 CTAD 的扩展(类型从调用点移动到函数参数)。此外,CTAD 已被证明可以与(非推导的)模板模板类一起使用。

最后因为是模板参数推导,在推导的一些扩展定义下。默认参数可以帮助推导出模板模板参数 ( std::tuple)。

于 2021-10-06T01:42:14.863 回答