在可变参数函数模板中,可以从模板参数包元素中推导出 return 类型

In a variadic function template can the return type be deduced from the template parameter pack elements

我正在尝试编写可变函数模板。 该函数的第一个参数是一个整数索引值。 其余(可变数量的)参数代表可变数量的参数。 此函数必须 return 位置索引处的参数。 例如,如果函数被调用为

`find_item(1, -1, "hello", 'Z', 10.03)`

必须return"hello"

如果调用为

find_item(2, -1, "hello", 'Z', 10.03)

必须return'Z'

如果调用为

find_item(3, -1, "hello", 'Z', 10.03, 'Q', 100)

它必须 return 10.03 等等....

我正在尝试类似以下代码的操作:

template <class T>
auto find_item(int inx, T val) { return val ;}

// GENERAL CASE
template <class T, class... Others>
auto find_item(int inx, T a1, Others ... others) {
    if(inx==0) return a1 ;
    else return print(inx-1, others...) ;
}

int main() {
    cout<<find_item(1, -1, "hello", string("abvd"), 10.03)<<endl ; ;
}

这段代码编译不通过,因为return类型不统一,无法推导。 我想知道是否有任何方法可以实现这一目标。或者它是一个无效的用例。 如果能做到,那怎么做。

这不是 C++ 惯用的处理方式,但您可以使用 std::variant 对返回不同类型进行建模。 variant_cast 是将 variant<T...> 转换为 variant<S...> 的助手,其中 S 是 T 的超集。

#include <variant>
#include <iostream>

template <class... Args>
struct variant_cast_proxy
{
    std::variant<Args...> v;

    template <class... ToArgs>
    operator std::variant<ToArgs...>() const
    {
        return std::visit([](auto&& arg) -> std::variant<ToArgs...> { return arg ; },
                          v);
    }
};

template <class... Args>
auto variant_cast(const std::variant<Args...>& v) -> variant_cast_proxy<Args...>
{
    return {v};
}


template <class T>
std::variant<T> find_item(int inx, T val) { return val ;}

// GENERAL CASE
template <class T, class... Others>
std::variant<T, Others...> find_item(int inx, T a1, Others ... others) {
    if(inx==0) return a1 ;
    else return variant_cast(find_item<Others...>(inx-1, others...)) ;
}



int main() {
    std::visit([](auto v){std::cout << v << std::endl;}, 
               find_item(1, -1, "hello", std::string("abvd"), 10.03)) ;
}

由于您的 return 类型由 运行 时间变量 inx 决定,因此无法知道 find_item 的确切 return 类型在编译时。

你需要做的是键入擦除。可以returnstd::variant<Args...>,根据inx的值,用相应的参数构造它。例如:

#include <variant>
#include <array>
#include <tuple>

template<class... Args>
constexpr auto find_item(std::size_t index, Args... args) {
  constexpr auto indices = []<std::size_t... Is>(std::index_sequence<Is...>) {
    using var_t = std::variant<std::integral_constant<std::size_t, Is>...>;
    return std::array{var_t{std::in_place_index<Is>}...};
  }(std::index_sequence_for<Args...>{});

  return std::visit([&...args = args](auto i) {
    return std::variant<Args...>{
      std::in_place_index<i>,
      std::get<i>(std::tuple(args...))};
  }, indices[index]);
}

Demo

另一种类型擦除的方法是使用函数指针:

#include <variant>
#include <array>
#include <tuple>

template<class... Args>
constexpr auto find_item(std::size_t index, Args... args) {
  constexpr auto funcs = []<std::size_t... Is>(std::index_sequence<Is...>) {
    return std::array{ +[](Args... args) { 
      return std::variant<Args...>(
        std::in_place_index<Is>, std::get<Is>(std::tuple(args...))); 
      }... };
  }(std::index_sequence_for<Args...>{});

  return funcs[index](args...);
}

Demo

I want to know if there is any way that this can be achieved.

没有

在 C/C++(静态类型语言)中,从函数 编辑的类型 return 必须 取决于 参数的类型,而不是值。

所以类型 return 来自以下调用

find_item(1, -1, "hello", 'Z', 10.03);
find_item(2, -1, "hello", 'Z', 10.03);
find_item(3, -1, "hello", 'Z', 10.03);

必须相同。

点。

您可以绕过此规则 return使用 std::variant(可以包含不同类型的值)或 std::any(可以包含通用值),但是独立于接收值确定的类型。

如果您将 type/value 的索引(在您的情况下是第一个参数)作为模板参数传递,则不同。

我的意思是:如果你按如下方式调用 find_item() 则不同

find_item<1>(-1, "hello", 'Z', 10.03);
find_item<2>(-1, "hello", 'Z', 10.03);
find_item<3>(-1, "hello", 'Z', 10.03);

现在find_item<1>()可以return一个char const *,find_item<2>()可以return一个charfind_item<3>()可以return一个double.

这是因为find_item<1>()find_item<2>()find_item<3>()是不同的函数,所以可以有不同的return类型。

但是...这样...我们几乎获得了从 std::tuple.

中提取值的 std::get<>()

不幸的是,只有当索引(模板参数)在编译时已知时,您才能使用此解决方案。

换句话说,你不能做出如下的东西

for ( auto i = 1 ; i < 4 ; ++i )
 { // .............NO! ---V
   auto value = find_item<i>(-1, "hello", 'Z', 10.03);
 }