当前位置: 首页 > 知识库问答 >
问题:

模板检查是否存在类成员函数?

田玉韵
2023-03-14

是否可以编写一个模板,根据是否在类上定义了某个成员函数来改变行为?

下面是我想写的一个简单的例子:

template<class T>
std::string optionalToString(T* obj)
{
    if (FUNCTION_EXISTS(T->toString))
        return obj->toString();
    else
        return "toString not defined";
}

因此,如果类T定义了ToString(),那么它将使用它;否则就不会。我不知道怎么做的神奇部分是“function_exists”部分。

共有2个答案

沈飞翼
2023-03-14

这个问题是老问题,但是在C++11中,我们有了一种新的方法来检查函数的存在性(或者任何非类型成员的存在性,真的),再次依赖于SFINAE:

template<class T>
auto serialize_imp(std::ostream& os, T const& obj, int)
    -> decltype(os << obj, void())
{
  os << obj;
}

template<class T>
auto serialize_imp(std::ostream& os, T const& obj, long)
    -> decltype(obj.stream(os), void())
{
  obj.stream(os);
}

template<class T>
auto serialize(std::ostream& os, T const& obj)
    -> decltype(serialize_imp(os, obj, 0), void())
{
  serialize_imp(os, obj, 0);
}

现在来解释一下。首先,如果decltype中的第一个表达式无效(也就是函数不存在),我使用表达式SFINAE从重载解析中排除serialize(_imp)函数。

void()用于使所有这些函数的返回类型void

0参数用于在os< 重载都可用的情况下(文字 0int类型,因此第一个重载更匹配)。

现在,您可能需要一个特征来检查函数是否存在。幸运的是,写这个很容易。但是,请注意,您需要为每一个不同的函数名自己编写一个特性。

#include <type_traits>

template<class>
struct sfinae_true : std::true_type{};

namespace detail{
  template<class T, class A0>
  static auto test_stream(int)
      -> sfinae_true<decltype(std::declval<T>().stream(std::declval<A0>()))>;
  template<class, class A0>
  static auto test_stream(long) -> std::false_type;
} // detail::

template<class T, class Arg>
struct has_stream : decltype(detail::test_stream<T, Arg>(0)){};

活生生的例子。

接着解释。首先,sfinae_true是一个帮助器类型,它基本上等同于编写decltype(void(std::declval ().stream(a0)),std::true_type{}) 。其优点只是它更短。
其次,结构has_stream:decltype(...)最终继承std::true_typestd::false_type,这取决于test_stream中的decltype检查是否失败。
最后,std::declval给出了您传递的任何类型的“值”,而不需要知道如何构造它。请注意,这只能在未求值的上下文中实现,如decltypesizeof等。

注意,不一定需要decltype,因为sizeof(以及所有未求值的上下文)得到了这种增强。只是decltype已经提供了一个类型,因此更加简洁。以下是其中一个重载的sizeof版本:

template<class T>
void serialize_imp(std::ostream& os, T const& obj, int,
    int(*)[sizeof((os << obj),0)] = 0)
{
  os << obj;
}

由于相同的原因,intlong参数仍然存在。数组指针用于提供可以使用sizeof的上下文。

郭曾笑
2023-03-14

是的,使用SFINAE,您可以检查给定的类是否提供了某个方法。工作代码如下:

#include <iostream>

struct Hello
{
    int helloworld() { return 0; }
};

struct Generic {};    

// SFINAE test
template <typename T>
class has_helloworld
{
    typedef char one;
    struct two { char x[2]; };

    template <typename C> static one test( decltype(&C::helloworld) ) ;
    template <typename C> static two test(...);    

public:
    enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
    
int main(int argc, char *argv[])
{
    std::cout << has_helloworld<Hello>::value << std::endl;
    std::cout << has_helloworld<Generic>::value << std::endl;
    return 0;
}

我刚刚用Linux和GCC4.1/4.3对它进行了测试。我不知道它是否可移植到运行不同编译器的其他平台。

 类似资料:
  • 问题内容: 我经常要检查一个对象是否有成员。一个示例是在函数中创建单例。为此,可以这样使用: 但您也可以这样做: 一种方法比另一种更好吗? 编辑: 添加了…但是,请注意,问题 不 在于如何制作单例,而在于如何检查对象中成员的存在。 编辑: 对于该示例,一种典型用法是: 然后是类型相同的对象,每次相同。并且,通常,该方法被调用多次。 问题答案: 这是两种不同的方法:№1是LBYL(飞跃前先看一下),

  • 我有一个模板化的C++类,它也有一个模板化的成员函数。这个成员函数的模板参数以特定的方式依赖于类的模板参数(请参阅下面的代码)。我正在为其模板参数的两个不同值实例化(而不是专门化)该类。一切都在这一点上进行。但是,如果我调用模板化的成员函数,对第一个实例化对象的调用只会编译,而不会编译第二个。似乎编译器没有为模板类的第二次实例化实例化模板化成员函数。我正在使用“g++filename.cpp”编译

  • 下一个;让我们调用模板化成员函数的不同实例: 最后是问题: Foo类的所有实例都来自同一个类?看来答案是肯定的但是...第一个Foo实例的末尾有f成员函数的两个重载:

  • 我试图做到以下几点:一个模板化的类应该提供一些函数,这些函数取决于它模板化的类型是否包含一个具有给定名称的成员变量。作为示例,下面的伪代码应该只在模板化的结构/类具有名为“id”的成员时才提供“printId()”: 通过研究SFINAE,traits,std::enable_if和StackOverflow,我认为这是可以做到的...但是我不知为什么没有将enable_if与以下问题中的片段结合

  • 我有一个问题,我想在下面的代码中专门化模板类的模板成员函数。这个问题的答案是模板类成员函数的显式特化,这似乎表明它无法完成。这是正确的吗,如果是这样,我可以使用任何解决方法,以便在编译时通过内联inc函数进行扩展? 非常感谢! g吐槽道: test2.cpp:32:13: 错误: 非命名空间作用域中的显式专用化 'struct IdxIterator' test2.cpp:32:25: 错误: 非

  • 是否有人知道此显式特化是否有效: clang 主干 (12/3/2013) 给出以下错误: f:...\test.cpp:36:20: 错误: 从类 'O' 中出线定义 “Fun” 没有定义 1生成错误。 任何来自标准的支持参考来证明你的答案将不胜感激! 注意:我有点惊讶这是一个错误——我认为应该为任何以< code >开始实例化“Fun”的模板参数族选择专门化 这是一个叮当的错误还是我期望中的错