【问题标题】:Overriding return type in function template specialization在函数模板特化中覆盖返回类型
【发布时间】:2013-04-01 11:17:27
【问题描述】:

我想专门化一个函数模板,以便返回类型根据模板参数的类型而变化。

class ReturnTypeSpecialization
{
public:
    template<typename T>
    T Item();
};

// Normally just return the template type
template<typename T>
T ReturnTypeSpecialization::Item() { ... }

// When a float is specified, return an int
// This doesn't work:
template<float>
int ReturnTypeSpecialization::Item() { ... }

这可能吗?我不能使用 C++11。

【问题讨论】:

  • 你想达到什么目的?
  • 我希望函数返回作为模板参数提供的类型,但在一种特殊情况下,我希望函数返回不同的类型。
  • 仅作记录:如果模板参数是从参数的类型推导出来的,而不是明确指定的,完成返回不同类型的更简单方法是使用函数重载。 (当然这在这个例子中是行不通的,因为没有参数)

标签: c++ templates


【解决方案1】:

由于特化必须与返回类型上的基本模板一致,您可以通过添加“返回类型特征”来实现这一点,这是一个您可以特化并从中提取真正返回类型的结构:

// in the normal case, just the identity
template<class T>
struct item_return{ typedef T type; };

template<class T>
typename item_return<T>::type item();

template<>
struct item_return<float>{ typedef int type; };
template<>
int item<float>();

Live example.

请注意,您可能希望坚持以下内容,因此您只需更新 item_return 特化中的返回类型。

template<>
item_return<float>::type foo<float>(){ ... }
// note: No `typename` needed, because `float` is not a dependent type

【讨论】:

  • 确保在实例化之前声明所有特化。实际上,这意味着必须在同一个头文件中声明特化,并且声明应该尽可能靠近。
  • @aschepler:非常好的观点。摆弄这种形式的专业化非常不稳定。
  • 如果T 是从属类型,即模板参数或从模板参数派生的类型,则trait&lt;T&gt;::type 中的type 将是从属名称。这里的“派生”是指typedef std::pair&lt;A, B&gt; T;,其中A 和/或B 是模板参数。
  • @didierc:嗯,C++ 中的依赖类型也可以依赖于一个值……如果它是一个依赖值的话。 ;) 无论是(非类型)模板参数,还是从模板参数派生的任何值。有依赖类型、依赖值和依赖名称。
  • 类型特征和这个typedef 技巧对我来说是全新的。我觉得豁然开朗。谢谢!
【解决方案2】:

在工作类中执行所有特化,并使用一个简单的函数作为将隐式特化的包装器。

#include <iostream>
using std::cout;

// worker class -- return a reference to the given value
template< typename V > struct worker
   {
   typedef V const & type;
   static type get( V const & v ) { return v; }
   };

// worker class specialization -- convert 'unsigned char' to 'int'
template<> struct worker<unsigned char>
   {
   typedef int type;
   static type get( unsigned char const & v ) { return v; }
   };

// mapper function
template< typename V > typename worker<V>::type mapper( V const & v )
   {
   return worker<V>::get(v);
   }

int main()
   {
   char a='A';
   unsigned char b='B';
   cout << "a=" << mapper(a) << ", b=" << mapper(b) << "\n";
   }

在此示例中,unsigned char 的特殊化导致它被转换为 int,以便 cout 将其显示为数字而不是字符,生成以下输出...

a=A, b=66

【讨论】:

  • 这与@Xeo 的答案具有相同的基本机制,但可能被认为更简洁,因为只需要一个专业化。实现的另一个特性是,正常情况本质上是一个 NOP——它只是返回对原始值的引用。
  • 我也喜欢这个解决方案。如果您想明确禁止通用解决方案并仅允许显式模板,则可以将第一个结构更改为仅template&lt; typename V &gt; struct worker;,没有正文。
【解决方案3】:

也许您可以使用以下技巧。鉴于这些简单的类型特征:

template<bool b, typename T, typename U>
struct conditional { typedef T type; };

template<typename T, typename U>
struct conditional<false, T, U> { typedef U type; };

template<typename T, typename U>
struct is_same { static const bool value = false; };

template<typename T>
struct is_same<T, T> { static const bool value = true; };

你可以编写你的类和专门的成员函数如下:

class ReturnTypeSpecialization
{
public:
    template<typename T>
    typename conditional<is_same<T, float>::value, int, T>::type 
    Item();
};

// Normally just return the template type
template<typename T>
typename conditional<is_same<T, float>::value, int, T>::type
ReturnTypeSpecialization::Item() { return T(); }

// When a float is specified, return an int
template<>
int ReturnTypeSpecialization::Item<float>() { return 1.0f; }

简单的测试程序(仅使用 C++11 进行验证):

int main()
{
    ReturnTypeSpecialization obj;
    static_assert(std::is_same<decltype(obj.Item<bool>()), bool>::value, "!");
    static_assert(std::is_same<decltype(obj.Item<float>()), int>::value, "!");
}

这是live example

【讨论】:

  • @Rapptz:好的,抱歉我错过了。但我相信这可以在不使用任何 C++11 特性的情况下重写(std::conditional 不难重写)
  • 我认为您的病情有误。此外,这对于扩展到更多专业来说是相当样板的,更糟糕​​的是,所有专业都必须事先知道。
  • // When a float is specified, return an int,但你专攻int并返回float
【解决方案4】:

您可以像这样进行模板特化:

template<typename T>
T item() {
    return T();
}

template<>
float item<float>() {
    return 1.0f;
}

【讨论】:

  • 这意味着T 也总是返回类型,从问题中的示例来看,似乎并非如此。
  • @Zeta:您可以完全专业化它们(也称为显式专业化)。
  • @Xeo:问题标题:“重写函数模板特化中的返回类型”。可能是问题内容中的错字。
  • @Xeo 是的,你是对的,这意味着 T 是返回类型。
  • 那么使用这种方法,要点或“catch”是需要为每种返回类型显式专门化模板吗?那正确吗?接受的答案允许一个人从一个或两个专业化动态覆盖返回类型是否也是正确的?就效率而言,但在编译和运行时-> 这种方法不是更好吗?编辑 - 我明白了 - 由于他们的用例,它对提问者来说真的不是更好。但是您的回答更有效地帮助了我。谢谢!
【解决方案5】:

您好,我尝试使用模板专业化来返回原语的参数值以及 std::string 数据,同时我得到了很多未解决的外部重新定义类型的错误。 所以如果有人遇到这样的事情,当他/她想要返回不同的数据类型(包括字符串)时,他/她可以使用类似下面的东西, 注意:两个模板函数都必须是头文件(*.h)的一部分... 所以我们在这里使用模板专业化字符串数据类型...... 在类内部作为内联成员,我们必须使用模板specialize方法,并且在同一个文件中我们也可以定义模板。

class ConfigFileParser
{
public:
    bool ParseConfigFile(const std::string& file_name);
    template <typename T>
    T GetParameterValue(const std::string key);
    template <>
    std::string GetParameterValue<std::string>(const std::string key)
    {
        std::string param_val = "";
        //do logical operation here... 
        return param_val;
    }
private:
    // private functions...

    // private data...
};

template <typename T>
T ConfigFileParser::GetParameterValue(const std::string key)
{
    T param_val = 0;
    std::stringstream ss;
    std::string val_str;
    // do some operation here...
    ss << val_str.c_str();
    ss >> param_val;

    return param_val;
}

【讨论】:

    猜你喜欢
    • 2020-11-05
    • 2018-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-03
    • 1970-01-01
    相关资源
    最近更新 更多