C++通过CRTP检测朋友课的私人成员
我有一个 CRTP 基类 (Bar),它由未指定的类继承。此派生类可能有也可能没有特定成员 (internal_foo),并且该特定成员可能有也可能没有其他成员 (test())。
在这种情况下,internal_foo 将始终是公共的,但是 test() 是私有的,但将 Bar 声明为好友。
我可以使用特征很好地检测internal_foo,因为它是公共的。但我无法检测到 test() 因为它是私有的,即使 Bar 是朋友。
下面的示例由于 test() 是公开的而有效:
template<class, class = void >
struct has_internal_foo : std::false_type {};
template<class T>
struct has_internal_foo<T,
void_t<
decltype(std::declval<T>().internal_foo)
>> : std::true_type {};
template<class, class = void>
struct internal_foo_has_test : std::false_type {};
template<class T>
struct internal_foo_has_test<T,
void_t<decltype(std::declval<T>().internal_foo.test())
>> : std::true_type {};
class InternalFoo
{
public:
void test()
{
}
};
class BadInternalFoo
{
};
template<class T>
class Bar
{
public:
template<class _T = T>
std::enable_if_t<conjunction<has_internal_foo<_T>, internal_foo_has_test<_T>>::value, void>
action()
{
static_cast<T&>(*this).internal_foo.test();
}
};
class Foo :
public Bar<Foo>
{
public:
InternalFoo internal_foo;
};
class BadFoo :
public Bar<BadFoo>
{
public:
BadInternalFoo internal_foo;
};
void test()
{
Foo foo;
BadFoo bad_foo;
foo.action(); // Compiles. As expected.
bad_foo.action(); // Does not compile. As expected.
}
但是,由于 test() 是私有的,所以下一个版本不起作用:
template<class, class = void >
struct has_internal_foo : std::false_type {};
template<class T>
struct has_internal_foo<T,
void_t<
decltype(std::declval<T>().internal_foo)
>> : std::true_type {};
template<class, class = void>
struct internal_foo_has_test : std::false_type {};
template<class T>
struct internal_foo_has_test<T,
void_t<decltype(std::declval<T>().internal_foo.test())
>> : std::true_type {};
class InternalFoo
{
public:
template<class T>
friend class Bar;
template<class, class>
friend struct internal_foo_has_test;
private:
void test()
{
}
};
class BadInternalFoo
{
};
template<class T>
class Bar
{
public:
template<class _T = T>
std::enable_if_t<conjunction<has_internal_foo<_T>, internal_foo_has_test<_T>>::value, void>
action()
{
static_cast<T&>(*this).internal_foo.test();
}
};
class Foo :
public Bar<Foo>
{
public:
InternalFoo internal_foo;
};
class BadFoo :
public Bar<BadFoo>
{
public:
BadInternalFoo internal_foo;
};
void test()
{
Foo foo;
BadFoo bad_foo;
foo.action(); // Does not compile
bad_foo.action(); // Does not compile
}
如上所示,我尝试过朋友也检测结构,但这没有帮助。
有办法做我想做的事吗?
理想情况下,我希望这个解决方案是可移植的,并且最多不使用 C++11、14 之外的任何内容。 (我已经实现了 void_t & 合取)
编辑:
建议的问题没有回答这个问题。这个问题想要检测一个成员是公共的还是私有的,并且只有在它是公共的情况下才访问它,我希望检测对朋友类的私有成员返回肯定。
I have a CRTP Base class (Bar) which is inherited by a unspecified class. This Derived class may or may not have specific member (internal_foo), and this specific member my or may not have another member (test()).
In this scenario internal_foo will always be public, however test() is private, but declaring Bar as a friend.
I can detect internal_foo using traits fine, because it is public. But I cannot detect test() due to it being private, even though Bar is a friend.
The below example works due to test() being public:
template<class, class = void >
struct has_internal_foo : std::false_type {};
template<class T>
struct has_internal_foo<T,
void_t<
decltype(std::declval<T>().internal_foo)
>> : std::true_type {};
template<class, class = void>
struct internal_foo_has_test : std::false_type {};
template<class T>
struct internal_foo_has_test<T,
void_t<decltype(std::declval<T>().internal_foo.test())
>> : std::true_type {};
class InternalFoo
{
public:
void test()
{
}
};
class BadInternalFoo
{
};
template<class T>
class Bar
{
public:
template<class _T = T>
std::enable_if_t<conjunction<has_internal_foo<_T>, internal_foo_has_test<_T>>::value, void>
action()
{
static_cast<T&>(*this).internal_foo.test();
}
};
class Foo :
public Bar<Foo>
{
public:
InternalFoo internal_foo;
};
class BadFoo :
public Bar<BadFoo>
{
public:
BadInternalFoo internal_foo;
};
void test()
{
Foo foo;
BadFoo bad_foo;
foo.action(); // Compiles. As expected.
bad_foo.action(); // Does not compile. As expected.
}
However this next version does not work, due to test() being private:
template<class, class = void >
struct has_internal_foo : std::false_type {};
template<class T>
struct has_internal_foo<T,
void_t<
decltype(std::declval<T>().internal_foo)
>> : std::true_type {};
template<class, class = void>
struct internal_foo_has_test : std::false_type {};
template<class T>
struct internal_foo_has_test<T,
void_t<decltype(std::declval<T>().internal_foo.test())
>> : std::true_type {};
class InternalFoo
{
public:
template<class T>
friend class Bar;
template<class, class>
friend struct internal_foo_has_test;
private:
void test()
{
}
};
class BadInternalFoo
{
};
template<class T>
class Bar
{
public:
template<class _T = T>
std::enable_if_t<conjunction<has_internal_foo<_T>, internal_foo_has_test<_T>>::value, void>
action()
{
static_cast<T&>(*this).internal_foo.test();
}
};
class Foo :
public Bar<Foo>
{
public:
InternalFoo internal_foo;
};
class BadFoo :
public Bar<BadFoo>
{
public:
BadInternalFoo internal_foo;
};
void test()
{
Foo foo;
BadFoo bad_foo;
foo.action(); // Does not compile
bad_foo.action(); // Does not compile
}
As seen above, I have tried to friend the detection struct too, but that didn't help.
Is there a way to do what I am trying to do?
Ideally I would like this solution to be portable, and not use anything beyond C++11, 14 at the most. (I have implemented void_t & conjunction)
Edit:
The suggested question does not answer this one. That question wants to detect whether a member is public or private, and only access it if it is public, I wish for the detection to return positive on a private member of a friend class.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
摘要和修复
看起来像 GCC 11 错误,您的第二次尝试实际上应该有效。
不过,我建议用两种方式重写
action
的定义,这样您甚至不需要成员检测习惯用法:请注意,我在
中使用
因此它依赖于模板参数并且可以被 SFINAEd 处理。另请注意,仍然可以指定任意返回类型而不使用任何_T
decltypeenable_if
。详细信息
我冒昧地在您的两个示例中添加了
#include
和using namespace std;
并使用 C++17,以便可以编译它们。评论部分的一些发现:
有一个更简单的复制示例: https://godbolt.org/z/T17dG3Mx1
上面的代码使用 Clang 14 和 gcc trunk 进行编译,但被 gcc 11 拒绝,并显示 3 条“静态断言失败”消息,每个断言各一条。但是,注释掉第一个
static_assert
会使所有三个编译器都接受该代码。因此,GCC 11(及更早版本)似乎尝试实例化模板并根据上下文进行访问检查。因此,如果第一个实例化位于友元之外,则无法访问
.test()
方法,并且结果会被缓存。但是,如果它位于friend void foo()
内部,则可以访问.test()
并且所有static_assert
都会成功。@Klaus 指出了最近的一个 GCC 错误,其修复似乎相关: https:// gcc.gnu.org/bugzilla/show_bug.cgi?id=96204
Summary and the fix
Looks like a GCC 11 bug and your second attempt should in fact work.
However, I recommend rewriting
action
's definition in either of two ways so you don't even need the member detection idiom:Note that I use
_T
inside thedecltype
so it's dependent on the template argument and can be SFINAEd. Also note that it's still possible to specify an arbitrary return return type without anyenable_if
s.Details
I took the liberty to prepend
#include <type_traits>
andusing namespace std;
to both of your examples and using C++17 so they can be compiled.Some findings from the comments section:
There is an easier reproduction example: https://godbolt.org/z/T17dG3Mx1
The code above compiles with Clang 14 and gcc trunk, but is rejected by gcc 11 with three "static assertion failed" messages, one for each assertion. However, commenting out the first
static_assert
makes all three compilers accept the code.So it seems like GCC 11 (and earlier) tries to instantiate the template and do access checks depending on the context. Hence, if the first instantiation is outside of a friend,
.test()
method is not accessible, and the result is kind cached. However, if it's inside thefriend void foo()
,.test()
is accessible and allstatic_assert
s succeed.@Klaus have pointed out a recent GCC bug whose fix seems relevant: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96204