C ++模板类,模板成员好友函数匹配规则

时间:2018-07-11 23:34:03

标签: c++ typetraits friend-function template-function

我有一个带有类化朋友函数声明的模板化类,当用更直接但看似等效的表达式表示时,其签名没有匹配:

link to example on online compiler

#include <type_traits>

template <typename Sig> class Base;
template <typename R, typename ... Args> class Base<R(Args...)> { };
template <typename Sig, typename T> class Derived;
template <typename Sig> struct remove_membership;

template <typename T, typename R, typename ... Args>
class Derived<R(Args...), T> : public Base<R(Args...)> {
  static void bar() { }

  // XXX: why are these two not equivalent, and only the 1st version successful?
  template <typename T2>
  friend auto foo(T2 const &) -> Base<typename
    remove_membership<decltype(&std::remove_reference_t<T2>::operator())>::type> *;
  template <typename T2>
  friend auto foo(T2 const &) -> Base<R(Args...)> *;
};

template <typename F, typename R, typename ... Args>
struct remove_membership<R (F::*)(Args...) const> {
  using type = R(Args...);
};

template <typename T>
auto foo(T const &) -> Base<typename 
remove_membership<decltype(&std::remove_reference_t<T>::operator())>::type> * 
{
  using base_param_t = typename remove_membership<
    decltype(&std::remove_reference_t<T>::operator())>::type;
  Derived<base_param_t, T>::bar();
  return nullptr;
}

int main(int, char **) { foo([](){}); } // XXX blows up if verbose friend decl. removed.

Derived<R(Args...), T>的内部成员定义(例如,在bar()的主体中),类型匹配,这使我感到困惑:

static_assert(std::is_same<Base<R(Args...)>, Base<typename  
  remove_membership<decltype(&std::remove_reference_t<T>::operator())>::type>>::value,
  "signature mismatch");

围绕模板类的模板成员函数(和好友函数)的声明和实例化是否存在规则,这些规则使这些先前的声明在某些或所有情况下都与众不同?

2 个答案:

答案 0 :(得分:2)

template <typename T2>
void foo(T2 const &)

template <typename T2>
auto foo(T2 const &)
-> std::enable_if_t<some_traits<T2>::value>;

有2种不同的重载。即使两个都返回void(有效时)。
第二次过载使用SFINAE。

(是的,模板函数只能在返回类型上不同于常规函数)。

您的版本不完全相同,但相似(&std::remove_reference_t<T>::operator()应该有效)

您可以使用更简单的模板好友功能:

template <typename T, typename R, typename ... Args>
class Derived<R(Args...), T> : public Base<R(Args...)> {
  static void bar() { }

  template <typename T2>
  friend auto foo(T2 const &) -> Base<R(Args...)>*;
};

template <typename T>
auto foo(T const &) -> Base<void()>* // friend with Derived<void(), U>
{
  using base_param_t = typename remove_membership<
    decltype(&std::remove_reference_t<T>::operator())>::type;
  Derived<base_param_t, T>::bar();
  return nullptr;
}

Demo

但是您必须实现模板foo的不同版本。

答案 1 :(得分:1)

问题可以减少为:

NumberOfMessagesSent

即使我们知道NumberOfMessagesReceived始终是template<class T> struct identity { using type=T; }; class X { int bar(); public: template<class T> friend T foo(); }; template<class T> typename identity<T>::type foo() { return X{}.bar(); } int main() { foo<int>(); // error: bar is a private member of X } ,但编译器并不知道这一点,因此假设这样做是错误的。代码后面的某个地方可能会identity<T>::type的一种特殊化,它会解析为T以外的某种类型。

因此,当编译器看到identity<T>的第二个声明时,将不会假定它与之前声明的同一个朋友T