调用非成员而不是成员函数

时间:2012-11-15 22:16:13

标签: c++

我有一个非常简单的问题:某个地方有一个功能

int size (const C & c)

至少可以通过参数依赖的名称查找找到。现在问题是:

struct B
{
    int size () { /* ... */ }

    void doSomething (const C & c)
    {
       int x = size (c); // <----------- problem!
       // ...
    }
}

这不起作用,因为名称查找在找到成员函数后停止。

我必须在指示的行中写一下,以便尝试调用 not 成员函数,但是,相反,如果成员函数没有,编译器会执行任何操作存在?

请注意,该解决方案正在撰写::size,因为这会阻止argument-dependent name lookup,并且仅在我知道size声明的位置时才有效。

进一步的复杂化:

我知道对于我使用下面模板化成员函数T的每个相关类型B::doSomething,某处会有一个函数

int size (const T & t)

至少可以通过参数依赖的名称查找找到。 B如下所示:

struct B
{
    int size () { /* ... */ }

    template<class T>
    void doSomething (const T & t)
    {
       int x = size (t); // <----------- problem!
       // ...
    }
}

我希望调用非成员函数(我确信它存在,但我无法确定它在哪里)。

4 个答案:

答案 0 :(得分:10)

这是一个众所周知的问题,其解决方案同样众所周知。我很惊讶它尚未被提及。如果您有非成员函数,例如:

class C;
size_t size( C const &c );

您可以使名称查找优先于具有using声明的成员函数:

struct B {
  size_t size();

  void foo( C const &c ) {
    using ::size;
    size_t sz = size(c);
  }
};

当编译器看到对size(c)的调用时,它会从最里面的范围开始,向外搜索名为size的内容。如果没有using声明,编译器会在全局命名空间中的非成员之前找到类范围中的成员函数,但using声明会更改此值。最里面的范围是函数本身,并且在成员函数之前找到using声明。

这样做的好处在于您仍然可以获得与参数相关的查找(ADL),因为对size(c)的实际调用是不合格的。这意味着您可以在模板中使用它:

template <class T>
void foo( T const &c ) {
  using ::size;
  size_t sz = size(c);
}

...即使正确的size函数在另一个命名空间中,它也可以由ADL找到。 using声明只需要引用某些 size函数,而不一定是您真正想要的函数。在一个可能调用成员的地方有一个默认实现是很正常的。下一版本的C ++标准(C ++ 17)几乎肯定会有一个std::size函数来完成它。一旦广泛使用,您就可以编写

using std::size;
size_t sz = size(c);

目前,您可以提供自己的默认实现,例如:

template <class C>
constexpr auto size( C const &c ) -> decltype(c.size()) {
  return c.size();
}

...或者您可以继续引用C的版本,并依赖ADL找到合适的版本。

答案 1 :(得分:6)

如果你不能重命名你自己的成员函数,你可以使用一个肮脏的技巧:

static inline int dirty_trick(C const & c)
{
    return size(c);
}

void B::doSomething(C const & c)
{
    int x = dirty_trick(c);

    // ...
}

答案 2 :(得分:3)

为了完整性,并加入理查德史密斯接受的答案:

我的解决方案现在看起来如下:

namespace adl {
   // This declaration's only purpose is the possibility to refer to
   // a non-member function named "size" in a using declaration.
   //
   // The signature does not matter, so we choose the easiest possible one.
   void size ();
}

struct B
{
    int size () { /* ... */ }

    template<class T>
    void doSomething (const T & t)
    {
       using adl::size;
       int x = size (t); // <----------- no problem anymore
       // ...
    }
};

这样我就不必包含任何实际可能不需要的标题。

答案 3 :(得分:0)

您可以使用SFINAE来检测您是否具有成员功能。

我使用此answer为大小准备此类模板测试:

template <typename B>
class has_size
{
    template <typename T, T> struct TypeCheck;

    typedef char Yes;
    typedef struct { char dummy[2]; } No;

    template <typename T> struct Size
    {
        typedef int (T::*fptr)(const C&);
    };

    template <typename T> static Yes HasSize(TypeCheck< typename Size<T>::fptr, &T::size >*);
    template <typename T> static No  HasSize(...);

public:
    static bool const value = (sizeof(HasSize<B>(0)) == sizeof(Yes));
};

现在,主要部分,2个功能 - 由于SFINAE只会选择其中一个:

#include <type_traits>
template <class B>
int callSize(B* obj, const C& c, typename std::enable_if<has_size<B>::value>::type* = 0)
{
    obj->size(c);
}
template <class B>
int callSize(B*, const C& c, typename std::enable_if<!has_size<B>::value>::type* = 0)
//                                                   ^ node this !
{
    return size(c);
}

现在,以自动魔术的方式 - 取决于你是否有B :: size(C) - 将调用全局或本地函数:

struct B
{
    int size () { /* ... */ };

    template <class U> friend int ::callSize(U*, const C&, typename std::enable_if<has_size<U>::value>::type*);
    template <class U> friend class has_size;
    void doSomething (const C & c)
    {
       int x = ::callSize(this, c); // <----------- problem!
       // ...
    }
private:
#ifdef LOCAL_SIZE
    int size (const C & c) {
       std::cout << "local size!\n";
       return 0;
     }
#endif
};

int main() {
    B b;
    b.doSomething(C());
}

输出Global size。获取Local size - 只需启用本地B::size(C)功能。用其他答案来证明这一点,即

工作示例here at ideone