C ++模板 - 使用" std :: is_same_v"而不是专门化和避免编译错误?

时间:2018-05-30 13:25:10

标签: c++ templates

我是模板的新手,我试图使用它们以避免重复非常相似的功能。

在下面的例子中,我做了一个简单而小巧的工作示例来说明我的问题。

特别是,我有两个结构(" solo"和#34; duo")。这些结构有一个共同的成员(a),其中一个有一个特定的成员(b)。

然后我有一个模板函数,它可以采用struct并打印成员a ...我希望它只能在结构类型为" duo"时才能打印成员b。

我的方式(使用std :: is_same_v)它没有编译。我读到可以使用专业化这样做,但我想知道是否有更优雅的方式?因为那时我有失去模板优势的感觉......但是我可能还没有获得模板的强大功能以及如何使用它们。

非常感谢你的帮助!

#include <iostream>
#include <string>
#include <type_traits>

struct solo{
  int a;     
};

struct duo : solo{
    int b;
};

template<class T>
void function(T test){
 std::cout<< std::to_string(test.a);
 if(std::is_same<T, duo>::value) std::cout<< std::to_string(test.b);
}

int main()
{
  solo testS;
  testS.a = 1;

  function(testS);
}

3 个答案:

答案 0 :(得分:2)

回答关于模板的问题(尽管在这个特定的应用程序中,由于许多原因,它不是正确的解决方案):

它写入时不起作用的原因是模板实例化在编译时发生,然后发生的唯一事情就是为模板参数计算std::is_same的值。因此,在function<solo>行的代码中

if(std::is_same<T, duo>::value) std::cout<< std::to_string(test.b);

就像

if(false) std::cout<< std::to_string(test.b);
由于b中没有成员test

无法编译。

为了使它工作,你需要两个模板,并在实例化模板时使用SFINAE选择正确的模板(并且因为函数模板不能部分专业化,你需要写下面的东西,这实际上是一种愚蠢的方式写两个重载。或者你可以完全专门化模板,但是你不会使用if_same)。

template<class T>
typename std::enable_if<!std::is_same<T, duo>::value, void>::type function(T test){
 std::cout<< std::to_string(test.a);
}

template<class T>
typename std::enable_if<std::is_same<T, duo>::value, void>::type function(T test){
 std::cout<< std::to_string(test.a);
 std::cout<< std::to_string(test.b);
}

此外,请注意is_same会查看变量的静态类型,因此如果您有一个solo&duo个对象,它仍会选择solo重载。

使用模板稍微不那么愚蠢的是编写一个函数模板,可以处理具有成员int b的任何类型。 这使用辅助元函数(结构,因此我们可以使用部分特化):

template <class T, class = int>
struct has_member_b : std::false_type {};

template <class T> 
struct has_member_b<T, decltype(std::declval<T>().b)> : std::true_type {};   

template<class T>
typename std::enable_if<has_member_b<T>::value, void>::type function(T test){
    std::cout<< std::to_string(test.a);
    std::cout<< std::to_string(test.b);
}

template<class T>
typename std::enable_if<!has_member_b<T>::value, void>::type function(T test) {
    std::cout<< std::to_string(test.a);
}

(请注意,两个版本都假设有一个成员a,如果没有,它将无法编译)

答案 1 :(得分:1)

duo扩展solo以来,您不需要模板。只需从function(solo)致电function(duo)

void function(solo test) {
   std::cout << std::to_string(test.a);
}

void function(duo test) {
   function((solo) test);
   std::cout << std::to_string(test.b);
}

答案 2 :(得分:0)

随着C ++ 17中constexpr if(cond)的引入,您可以实现自己的目标。 constexpr if(cond)在编译时进行评估,因此您可以根据参数类型选择要执行的操作。以下摘录提供了一个说明。

#include <iostream>
#include <string>
#include <type_traits>

struct solo{
  int a;     
};

struct duo : solo{
    int b;
};

template<class T>
void function(T test){ 
 if constexpr (std::is_same<T, duo>::value) 
    std::cout<< std::to_string(test.b)<<"\n";
 else if constexpr (std::is_same<T, solo>::value) 
    std::cout<< std::to_string(test.a)<<"\n";
}

int main()
{
  solo test1;
  test1.a = 1;

  duo test2;
  test2.b = 2;

  function(test1);
  function(test2);
}