C++模板class、模板成员友元函数匹配规则
C++ template class, template member friend function matching rules
我有一个模板化的 class 和一个模板化的友元函数声明,当用更直接但看似等价的表达式声明时,它的签名没有匹配:
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");
是否有关于模板 class 模板成员函数(和友元函数)声明和实例化的规则,使这些前面的声明在某些或所有情况下都不同?
问题可以简化为:
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
.
以外的某种类型
因此,当编译器看到 foo
的第二个声明时,它不会假定它是之前声明的同一个朋友 foo
。
template <typename T2>
void foo(T2 const &)
template <typename T2>
auto foo(T2 const &)
-> std::enable_if_t<some_traits<T2>::value>;
是2个不同的重载。即使两者 return void
(有效时).
第二次重载使用 SFINAE。
(是的,与常规函数相反,模板函数只能在 return 类型上有所不同)。
您的版本不相同但相似(&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;
}
但您必须实施不同版本的模板 foo
。
我有一个模板化的 class 和一个模板化的友元函数声明,当用更直接但看似等价的表达式声明时,它的签名没有匹配:
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");
是否有关于模板 class 模板成员函数(和友元函数)声明和实例化的规则,使这些前面的声明在某些或所有情况下都不同?
问题可以简化为:
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
.
因此,当编译器看到 foo
的第二个声明时,它不会假定它是之前声明的同一个朋友 foo
。
template <typename T2>
void foo(T2 const &)
template <typename T2>
auto foo(T2 const &)
-> std::enable_if_t<some_traits<T2>::value>;
是2个不同的重载。即使两者 return void
(有效时).
第二次重载使用 SFINAE。
(是的,与常规函数相反,模板函数只能在 return 类型上有所不同)。
您的版本不相同但相似(&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;
}
但您必须实施不同版本的模板 foo
。