【问题标题】:Detecting a function in C++ at compile time在编译时检测 C++ 中的函数
【发布时间】:2012-02-13 06:38:03
【问题描述】:

有没有一种方法,可能是使用模板、宏或两者的组合,我可以将函数一般地应用于不同类的对象,但如果它们没有特定的函数,它们会以不同的方式响应?

我特别想应用一个函数,如果对象具有该函数,它将输出对象的大小(即集合中的对象数量),但如果对象具有该函数,则会输出一个简单的替换(例如“N/A”),如果对象没有。即

NO_OF_ELEMENTS( mySTLMap ) -----> [ calls mySTLMap.size() to give ] ------>  10
NO_OF_ELEMENTS( myNoSizeObj ) --> [ applies compile time logic to give ] -> "N/A"

我希望这可能类似于静态断言,尽管我显然希望编译不同的代码路径而不是在构建阶段失败。

【问题讨论】:

  • @AlessandroPezzato:随着 C++11 的敲门声,链接的答案中出现了尚未探索的新机会。
  • 感谢您的所有回答和建议。遗憾的是,由于客户端的限制,我无法在我的解决方案中使用 C++11 或 Boost,这是一种耻辱。尽管如此,根据你们提供的所有信息,我能够做到这一点并对其进行扩展,以提供一个函数,使用私有的嵌入式模板类部分地流出 size() 或“”专门用于“真实”条件。我现在很高兴!!谢谢大家!

标签: c++ templates static macros


【解决方案1】:

据我了解,您希望进行通用测试以查看类是否具有某个成员函数。这可以在 C++ 中使用SFINAE 来完成。在 C++11 中它非常简单,因为您可以使用 decltype:

template <typename T>
struct has_size {
private:
    template <typename U>
    static decltype(std::declval<U>().size(), void(), std::true_type()) test(int);
    template <typename>
    static std::false_type test(...);
public:
    typedef decltype(test<T>(0)) type;
    enum { value = type::value };
};

如果你使用C++03,由于缺少decltype,会有点困难,所以你必须滥用sizeof

template <typename T>
struct has_size {
private:
    struct yes { int x; };
    struct no {yes x[4]; };
    template <typename U>
    static typename boost::enable_if_c<sizeof(static_cast<U*>(0)->size(), void(), int()) == sizeof(int), yes>::type test(int);
    template <typename>
    static no test(...);
public:
    enum { value = sizeof(test<T>(0)) == sizeof(yes) };
};

当然这使用了Boost.Enable_If,这可能是一个不需要的(和不必要的)依赖。但是自己写enable_if 非常简单:

template<bool Cond, typename T> enable_if;
template<typename T> enable_if<true, T> { typedef T type; };

在这两种情况下,方法签名 test&lt;U&gt;(int) 仅在 U 具有 size 方法时可见,否则评估 decltypesizeof(取决于您使用的版本)将失败,然后将删除该方法(由于SFINAE。冗长的表达式std::declval&lt;U&gt;().size(), void(), std::true_type()是对C++逗号运算符的滥用,它将返回逗号分隔列表中的最后一个表达式,因此确保类型为对于 C++11 变体,称为 std::true_type(对于 C++03 变体,sizeof 评估 int)。中间的 void() 只是为了确保没有奇怪的重载干扰评估的逗号运算符。

当然,如果T 有一个size 方法可以在没有参数的情况下调用,那么这将返回true,但不保证返回值。我假设您可能只想检测那些不返回 void 的方法。这可以通过对test(int) 方法的轻微修改轻松完成:

// C++11
template <typename U>
static typename std::enable_if<!is_void<decltype(std::declval<U>().size())>::value, std::true_type>::type test(int);
//C++03
template <typename U>
static typename std::enable_if<boost::enable_if_c<sizeof(static_cast<U*>(0)->size()) != sizeof(void()), yes>::type test(int);

【讨论】:

  • 有趣...但是void() 是什么意思?
  • @MatthieuM.:void() 不是绝对需要的。但是,它可以确保重载的逗号运算符不会发生奇怪的事情(在这种情况下不太可能,但这是我用于检查所有方法存在的一般方式,所以我想我会保留它)。
  • 我简单地认为可能是这种情况,但是当你添加true_type 时,它似乎更像是一个被遗忘的东西 :) 至于采用decltype 方式,它使事情变得如此函数很容易,将特征定义为constexpr 函数更容易,或者只是绕过特征并直接使用它,请参阅我的答案:)
【解决方案2】:

前段时间讨论过constexpr的能力。我想是时候使用它了:)

使用constexprdecltype 很容易设计一个特征:

template <typename T>
constexpr decltype(std::declval<T>().size(), true) has_size(int) { return true; }

template <typename T>
constexpr bool has_size(...) { return false; }

事实上如此简单,以至于特征失去了大部分价值:

#include <iostream>
#include <vector>

template <typename T>
auto print_size(T const& t) -> decltype(t.size(), void()) {
  std::cout << t.size() << "\n";
}

void print_size(...) { std::cout << "N/A\n"; }

int main() {
  print_size(std::vector<int>{1, 2, 3});
  print_size(1);
}

In action:

3
N/A

【讨论】:

  • 我可以在 Grizzly 的回答中看到 void() 的使用,但是在这里?似乎是多余的,因为无论如何“可能的”逗号运算符都没有第二个参数。剩下的?
  • 我不确定这是否是编写特征的更好方法。与将其封装在结构中的“常见”方法相比,您只需避免围绕方法编写结构并将结果值放入枚举或constexpr bool。另一方面,has_size 以这种方式 IMO 的签名不太理想。 print_size 的血迹相同。
  • @Xeo:print_size 中的void() 用于给它一个返回值void 而不是decltype(declval&lt;T&gt;().size())
  • @Grizzly:我认为这是一种不同的方式。它肯定是新的,所以我预计会有一些阻力。我给出了一些理由,而我认为可以将特征移至函数here。几年前我会接受你的课程,但现在我正在尝试探索 C++11 的可能性,尤其是在减少混乱和获得新功能方面。
  • 请注意,您可以通过使用尾随返回类型进一步减少混乱:auto print_size(T const&amp; t) -&gt; decltype(t.size(), void())
【解决方案3】:

这可以使用称为SFINAE 的技术来完成。在您的特定情况下,您可以使用Boost.Concept Check 来实现它。您必须编写自己的概念来检查size-方法。或者,您可以使用现有的概念,例如 Container,其中需要 size 方法。

【讨论】:

  • 我很惊讶这个答案没有得到最高票。 SFINAE 是去这里的方式,恕我直言。
  • @AzzA:尽管我不喜欢“你可以做类似 之类的事情”风格的答案,但这里的大多数答案实际上都显示了 SFINAE 实践......
【解决方案4】:

你可以这样做

template< typename T>
int getSize(const T& t)
{
    return -1;
}

template< typename T>
int getSize( const std::vector<T>& t)
{
    return t.size();
}

template< typename T , typename U>
int getSize( const std::map<T,U>& t)
{
    return t.size();
}

//Implement this interface for 
//other objects
class ISupportsGetSize
{
public:
    virtual int size() const= 0;
};

int getSize( const ISupportsGetSize & t )
{
    return t.size();
}

int main()
{
    int s = getSize( 4 );
    std::vector<int> v;
    s = getSize( v );

    return 0;
}

基本上,最通用的实现总是返回 -1 或“NA”,但对于矢量和地图,它将返回大小。由于最通用的总是匹配,因此永远不会出现构建时间失败

【讨论】:

    【解决方案5】:

    给你。std::cout替换为你喜欢的输出。

    template <typename T>
    class has_size
    {
        template <typename C> static char test( typeof(&C::size) ) ;
        template <typename C> static long test(...);
    
    public:
        enum { value = sizeof(test<T>(0)) == sizeof(char) };
    };
    
    template<bool T>
    struct outputter
    {
        template< typename C >
        static void output( const C& object )
        {
            std::cout << object.size();
        }
    };
    
    template<>
    struct outputter<false>
    {
        template< typename C >
        static void output( const C& )
        {
            std::cout << "N/A";
        }
    };
    
    
    template<typename T>
    void NO_OF_ELEMENTS( const T &object )
    {
        outputter< has_size<T>::value >::output( object );
    }
    

    【讨论】:

    • 这行不通。 object.size() 会导致失败。您需要将打印委托给 SFINAE 的结构。
    • @MatthieuM。是的!我忽略了这一点。谢谢。现已修复。
    【解决方案6】:

    你可以试试这样的:

    #include <iostream>
    #include <vector>
    
    
    
    template<typename T>                                                                
    struct has_size                                                                 
    {                                                                                   
      typedef char one;                                                                 
      typedef struct { char a[2]; } two;                                                
    
      template<typename Sig>                                                            
      struct select                                                                     
      {                                                                                 
      };                                                                                
    
      template<typename U>                                                              
      static one check (U*, select<char (&)[((&U::size)!=0)]>* const = 0);     
      static two check (...);                                                           
    
      static bool const value = sizeof (one) == sizeof (check (static_cast<T*> (0)));   
    };
    
    
    
    struct A{ };
    int main ( )
    {
        std::cout << has_size<int>::value << "\n";
        std::cout << has_size<A>::value << "\n";
        std::cout << has_size<std::vector<int>>::value << "\n";
    }
    

    但是你必须小心,这在size 被重载时不起作用,在它是模板时也不起作用。当你可以使用 C++11 时,你可以将上面的 sizeof 技巧替换为 decltype 魔术

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-28
      • 2013-09-29
      • 2011-03-19
      • 2017-03-18
      • 1970-01-01
      • 2011-07-13
      相关资源
      最近更新 更多