模板参数默认为更高版本

Template parameter default to a later one

本文关键字:版本 高版本 参数 默认      更新时间:2023-10-16

这个链接没有回答我的问题,所以我会在这里问:

基本上我想写一个模板函数

template <typename Out, typename In>
Out f(In x);

在这里,我在调用f时总是需要指定Out。 我不想每次都这样做,所以我基本上想要

template <typename Out = In, typename In>
Out f(In x);

这意味着如果我不指定Out,它将默认为 In 。 但是,这在 C++11 中是不可能的。

所以我的问题是,有没有办法达到效果:

  1. 调用f(t)将实例化f<T,T>(t)或更一般的f<typename SomeThing<T>::type, T>
  2. 调用f<U>(t)将实例化f<U, T>(t)

您可能永远不想指定In而是推断它,对吧?

在这种情况下,您需要重载函数:

template <typename Out, In>
Out f(In x);
template <typename T>
T f(T x);

称之为:

f(42);
f<float>(42);

。但不幸的是,这对f<int>(42)来说是模棱两可的.无论如何,我们都可以使用 SFINAE 来适当地禁用其中一个重载:

template <
    typename Out,
    typename In,
    typename = typename std::enable_if<not std::is_same<Out, In>::value>::type
>
Out f(In x);
template <typename T>
T f(T x);

为了避免实现中的冗余,让两个函数调度到一个共同的实现,f_impl

下面是一个工作示例:

template <typename Out, typename In>
Out f_impl(In x) {
    std::cout << "f<" << typeid(Out).name() <<
                 ", " << typeid(In).name() <<
                 ">(" << x << ")n";
    return x;
}
template <
    typename Out,
    typename In,
    typename = typename std::enable_if<not std::is_same<Out, In>::value>::type
>
Out f(In x) {
    std::cout << "f<Out, In>(x):t ";
    return f_impl<Out, In>(x);
}
template <typename T>
T f(T x) {
    std::cout << "f<T>(x):t ";
    return f_impl<T, T>(x);
}

int main() {
    f(42);
    f<float>(42);
    f<int>(42);
}

这里可能不需要它,但这里有一个经典的技术:

struct Default
{
  template <typename Argument, typename Value>
    struct Get {
      typedef Argument type;
    };
  template <typename Value>
    struct Get <Default, Value> {
      typedef Value type;
    };
};
template <typename Out = Default, typename In>
typename Default::Get<Out, In>::type f(In x);

我这里有一个完美的解决方案! f<const int&>不起作用,因为函数无法返回对临时的引用,与此处使用的技术无关。

[hidden]$ cat a.cpp
#include <iostream>
#include <type_traits>
#include <typeinfo>
using namespace std;
template <typename Out, typename In>
Out f_impl(In x) {
  cout << "Out=" << typeid(Out).name() << " " << "In=" << typeid(In).name() << endl;
  return Out();
}
template <typename T, typename... Args>
struct FirstOf {
  typedef T type;
};
template <typename T, typename U>
struct SecondOf {
  typedef U type;
};
template <typename... Args, typename In>
typename enable_if<sizeof...(Args) <= 1, typename FirstOf<Args..., In>::type>::type f(In x) {
  typedef typename FirstOf<Args..., In>::type Out;
  return f_impl<Out, In>(x);
}
template <typename... Args, typename In>
typename enable_if<sizeof...(Args) == 2, typename FirstOf<Args...>::type>::type f(In x) {
  typedef typename FirstOf<Args...>::type Out;
  typedef typename SecondOf<Args...>::type RealIn;
  return f_impl<Out, RealIn>(x);
}
int main() {
  f(1);
  f(1.0);
  f<double>(1);
  f<int>(1.0);
  f<int>(1);
  f<const int>(1);
  f<int, double>(1);
  f<int, int>(1);
  f<double, double>(1);
}
[hidden]$ g++ -std=c++11 a.cpp
[hidden]$ ./a.out
Out=i In=i
Out=d In=d
Out=d In=i
Out=i In=d
Out=i In=i
Out=i In=i
Out=i In=d
Out=i In=i
Out=d In=d
相关文章: