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

Hug*_*ugo 5 c++ templates

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

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

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

然后我有一个模板函数,可以采取结构和打印成员...并且我希望它只能在结构类型为"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);
}
Run Code Online (Sandbox Code Playgroud)

yad*_*dhu 8

通过 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);
}
Run Code Online (Sandbox Code Playgroud)


drR*_*rtz 7

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

它不像你写的那样工作的原因是模板实例化发生在编译时,唯一发生的事情std::is_same是为模板参数计算的值。因此,在function<solo>该行的代码中

if(std::is_same<T, duo>::value) std::cout<< std::to_string(test.b);
Run Code Online (Sandbox Code Playgroud)

会像

if(false) std::cout<< std::to_string(test.b);
Run Code Online (Sandbox Code Playgroud)

这不编译因为没有成员btest

为了使它工作,你需要两个模板,并在实例化模板时使用 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);
}
Run Code Online (Sandbox Code Playgroud)

此外,请注意 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);
}
Run Code Online (Sandbox Code Playgroud)

(请注意,两个版本都假定存在成员a,否则将无法编译)