如何在C++中比较两个类型名称是否相等?

26
假设我有一个函数模板,比如说:
template<typename T>
func(T a, T b, ...) {
  ...
  for (const auto &single : group) {
    ...
    auto c = GivenFunc1(a, b, single, ...);
    ...      }
  ...
}

然而,如果T是一个特殊类型,比如“SpecialType”,我希望用“GivenFunc2”计算c而不是“GivenFunc1”。但是,我不想为“SpecialType”写一个专门的实现,因为那将导致大量代码重复。所以我希望模板函数像这样:

template<typename T>
func(T a, T b, ...) {
  ...
  for (const auto &single : group) {
    ...
    auto c = (T == SpecialType) ? GivenFunc2(a, b, single, ...)
                                : GivenFunc1(a, b, single, ...);
    ...      }
  ...
}

当然,这段代码不能编译,因为“T == SpecialType”无效。那么我该如何以优雅的方式编写它?

4
可以考虑写一个模板特化,而不是使用原来的方法? - πάντα ῥεῖ
1
可能的重复问题:1 2 3 4 - user202729
5个回答

32

这很简单:

auto c = std::is_same_v<T, SpecialType> ? GivenFunc2(a, b, single, ...)
                                        : GivenFunc1(a, b, single, ...);

如果您无法使用C++17,请将 std::is_same_v<...> 替换为 std::is_same<...>::value

但是,为了使这种方法起作用,对于您想要使用的每个 T,两个函数调用都必须有效,即使实际上其中一个不会被执行。


如果不是这种情况,您可以使用 if constexpr

your_type_here c;
if constexpr (std::is_same_v<T, SpecialType>)
    c = GivenFunc2(a, b, single, ...);
else
    c = GivenFunc1(a, b, single, ...);

(这仅适用于C++17。)


8
我建议无论如何都使用if constexpr。确实,编译器应该能够优化掉检查的过程,但检查其他函数调用的有效性可能会很昂贵,特别是如果涉及到额外的模板实例化。而且,不使用if constexpr可能会成为这些函数的维护问题:任何修改它们的人都必须考虑到它们需要对永远不会被调用的参数也有效。 - user743382
很棒的答案,获得了6个赞。不幸的是,虽然我很希望在我使用的编译器上看到这个工作,但它不起作用。这是一个C++17的模板/函数吗? - TrebledJ
3
是的,std::is_same_v 是 C++17 中引入的。如果你没有这个函数,可以使用 std::is_same<...>::value 代替。if constexpr 也是 C++17 中的特性,如果需要其他选择,请查看其他回答。 - HolyBlackCat

18
如果您能使用C++17,您可以通过constexpris_same以非常干净的方式实现结果:
template<typename T>
func(T a, T b, ...) {
  // ...

  if constexpr (std::is_same_v<T, SpecialType>) {
    // call GivenFunc2
  } else {
    // call GivenFunc1
  } 

  // ...
}

在 C++17 之前,您可以使用 SFINAE 或“TAG Dispatching”等技术来实现相同的结果。

此外,您可以仅针对函数调用引用的代码部分进行 特化(简单并避免代码重复)。

这里有一个简短的示例 here

template <typename T>
struct DispatcherFn {
  auto operator()(const T&, int) {
      // call GivenFunc1
  }
};

template <>
struct DispatcherFn<SpecialType> {
  auto operator()(const SpecialType&, int) {
    // GivenFunc2
  }
};

template <typename T>
void func(const T& t) {
  // ... code ...
  auto c = DispatcherFn<T>()(t, 49);  // specialized call
}

12

您可以始终使用模板特化而不是对模板参数进行类型比较。这里是一个简化的,可工作的示例:

#include <iostream>
#include <string>

template<typename T>
int GivenFunc1(T a, T b) {
     std::cout << "GivenFunc1()" << std::endl;
     return 0;
}

template<typename T>
int GivenFunc2(T a, T b) {
     std::cout << "GivenFunc2()" << std::endl;
     return 1;
}

template<typename T>
void func(T a, T b) {
    auto c = GivenFunc2(a, b);
    std::cout << c << std::endl;
}

template<>
void func(std::string a, std::string b) {
    auto c = GivenFunc1(a, b);
    std::cout << c << std::endl;
}

int main() {
    func(2,3);
    std::string a = "Hello";
    std::string b = "World";
    func(a,b);
}

点击此处在线查看它的运行效果。


6
中,最佳解决方案是 if constexpr
中,这个方法可行:
template<class V>
auto dispatch( V const& ) {
  return [](auto&&...targets) {
    return std::get<V{}>( std::forward_as_tuple( decltype(targets)(targets)... ) );
  };
}

然后:
 auto c = dispatch( std::is_same<T, SpecialType>{} )
 (
   [&](auto&& a, auto&& b){ return GivenFunc2(a, b, single...); },
   [&](auto&& a, auto&& b){ return GivenFunc1(a, b, single, ...); }
 )( a, b );

它可以做你想要的事情。(它也是一个返回函数的函数)

实时示例

dispatch 在编译时选择其中一个lambda并返回。然后我们使用ab调用选择的lambda。因此,只有有效的lambda才会使用ab的类型进行编译。


1
GivenFunc1 转换为一个函数对象并进行特化。
template <class T>
class GivenFunc
{
    X operator()(T a, T b, Y single)
    {
        ...
    }
}

template <>
class GivenFunc<SpecialType>
{
    X operator()(SpecialType a, SpecialType b, Y single)
    {
        ...
    }
}

然后你可以说。
auto c = GivenFunc<T>()(a, b, single);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接