仅对定义了功能的类型在功能模板内执行功能


13

我有一个函数模板,它接受许多不同的类型。在这些类型中,只有其中一种具有getInt()功能。因此,我希望代码仅针对该类型运行函数。请提出解决方案。谢谢

#include <type_traits>
#include <typeinfo>

class X {
    public:
    int getInt(){
        return 9;
    }
};

class Y{

};

template<typename T>
void f(T& v){
    // error: 'class Y' has no member named 'getInt'
    // also tried std::is_same<T, X>::value 
    if(typeid(T).name() == typeid(X).name()){
        int i = v.getInt();// I want this to be called for X only
    }
}

int main(){
    Y y;
    f(y);
}

与您的问题无关,但是type_info结构具有一个相等比较运算符,因此也typeid(T) == typeid(X)应该工作。
一些程序员老兄

5
使用:if constexpr有条件is_same_v<T,X>
rafix07

解决方案将在今年晚些时候正式通过Concepts变得更加优雅。我知道,目前不是超级有帮助。
sweenish

有很多方法可以解决您的问题。上面提到的一对。你也可以使用性状不同的变种,以查看是否有型有一个可调用的getInt成员。仅在stackoverflow.com上就必须有很多问题,如果您稍作搜索,如何查看结构或类是否具有特定的成员函数。
一些程序员老兄

Answers:


10

如果您希望能够f为所有具有function member的类型调用函数getInt,而不仅仅是X,则可以为function声明2个重载f

  1. 具有getInt成员功能的类型,包括类X

  2. 对于所有其他类型,包括class Y

C ++ 11 / C ++ 17解决方案

考虑到这一点,您可以执行以下操作:

#include <iostream>
#include <type_traits>

template <typename, typename = void>
struct has_getInt : std::false_type {};

template <typename T>
struct has_getInt<T, std::void_t<decltype(((T*)nullptr)->getInt())>> : std::is_convertible<decltype(((T*)nullptr)->getInt()), int>
{};

class X {
public:
    int getInt(){
        return 9;
    }
};

class Y {};

template <typename T,
          typename std::enable_if<!has_getInt<T>::value, T>::type* = nullptr>
void f(T& v) {
    // only for Y
    std::cout << "Y" << std::endl;
}

template <typename T,
          typename std::enable_if<has_getInt<T>::value, T>::type* = nullptr>
void f(T& v){
    // only for X
    int i = v.getInt();
    std::cout << "X" << std::endl;
}

int main() {
    X x;
    f(x);

    Y y;
    f(y);
}

现场检查一下。

请注意,它std::void_t是C ++ 17中引入的,但是如果您仅限于C ++ 11,那么很容易自己实现void_t

template <typename...>
using void_t = void;

这是C ++ 11版本live

我们在C ++ 20中拥有什么?

C ++ 20带来了很多美好的事物,其中之一就是概念。以上对于C ++ 11 / C ++ 14 / C ++ 17有效的事情可以在C ++ 20中大大减少:

#include <iostream>
#include <concepts>

template<typename T>
concept HasGetInt = requires (T& v) { { v.getInt() } -> std::convertible_to<int>; };

class X {
public:
    int getInt(){
        return 9;
    }
};

class Y {};

template <typename T>
void f(T& v) {
    // only for Y
    std::cout << "Y" << std::endl;
}

template <HasGetInt T>
void f(T& v){
    // only for X
    int i = v.getInt();
    std::cout << "X" << std::endl;
}

int main() {
    X x;
    f(x);

    Y y;
    f(y);
}

现场检查一下。


在C ++ 17之前,这种实现void_t会导致某些旧编译器出现问题(如链接所指出)。
Jarod42

不一定要写两个重载(用“可以”替换“需要”会更好,恕我直言)
idclev 463035818

@ idclev463035818已更新。谢谢
NutCracker

1
@SSAnne更新
NutCracker

1
概念定义不正确。您正在将结果分配给一个int,所以概念应该是template<typename T> concept HasGetInt = requires (T& v) { {v.getInt()} -> std::convertible_to<int>; };
Hui

8

您可以if constexpr从C ++ 17使用:

template<typename T>
void f(T& v){
    if constexpr(std::is_same_v<T, X>) { // Or better create trait has_getInt
        int i = v.getInt();// I want this to be called for X only
    }
    // ...
}

在此之前,您将必须使用重载和SFINAE或标签分派。


if constexpr是C ++ 17的功能。
Andrey Semashev

但是,这仅适用于课程X
NutCracker

问题现在仅更新为C ++ 11 / C ++ 14
NutCracker

@NutCracker:不好更新标签/问题,因此使现有答案无效...(即使警告也可以)。
Jarod42

我刚刚更新了标签...问题标题由OP更新了
NutCracker

7

保持简单和重载。至少从C ++ 98起就开始工作了...

template<typename T>
void f(T& v)
{
    // do whatever
}

void f(X& v)
{
    int result = v.getInt();
}

如果只有一种类型具有getInt功能,这就足够了。如果还有更多,那就不再那么简单了。有几种方法可以做到,这是一种:

struct PriorityA { };
struct PriorityB : PriorityA { };

template<typename T>
void f_impl(T& t, PriorityA)
{
    // generic version
}

// use expression SFINAE (-> decltype part)
// to enable/disable this overload
template<typename T>
auto f_impl(T& t, PriorityB) -> decltype(t.getInt(), void())
{
    t.getInt();
}

template<typename T>
void f(T& t)
{
    f_impl(t, PriorityB{ } ); // this will select PriorityB overload if it exists in overload set
                              // otherwise PriorityB gets sliced to PriorityA and calls generic version
}

带有诊断输出的实时示例。


1
在这种情况下,这是可行的,因为只有一个重载(对于X),但是,如果getInt将来有更多类似类型的成员,这不是一个好习惯。您可能需要注意
NutCracker

@NutCracker这样做了。
jrok
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.