gpt4 book ai didi

C++ 使用 CRTP 检测 friend 类的私有(private)成员

转载 作者:行者123 更新时间:2023-12-05 04:31:45 28 4
gpt4 key购买 nike

我有一个由未指定类继承的 CRTP 基类 (Bar)。此派生类可能有也可能没有特定成员 (internal_foo),并且此特定成员可能有也可能没有其他成员 (test())。

在这种情况下,internal_foo 将始终是公开的,但是 test() 是私有(private)的,但将 Bar 声明为 friend 。

我可以很好地使用特征检测internal_foo,因为它是公开的。但是我无法检测到 test(),因为它是私有(private)的,即使 Bar 是 friend 。

由于 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() 是私有(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
}

如上所示,我也尝试过将检测结构加为好友,但这没有帮助。

有没有办法做我想做的事?

理想情况下,我希望这个解决方案是可移植的,并且最多不使用 C++11、14 以外的任何东西。 (我已经实现了 void_t & conjunction)

编辑:

建议的问题没有回答这个问题。该问题想要检测一个成员是公共(public)成员还是私有(private)成员,并且只有在它是公共(public)成员时才访问它,我希望检测在 friend 类的私有(private)成员上返回正面

最佳答案

总结和修复

看起来像是 GCC 11 错误,您的第二次尝试应该确实有效。

但是,我建议重写 action以两种方式之一定义,因此您甚至不需要成员检测惯用语:

// Way 1
template<class _T = T>
decltype(std::declval<_T&>().internal_foo.test()) action() {
static_cast<T&>(*this).internal_foo.test();
}

// Way 1, with a different return type via the comma operator
template<class _T = T>
decltype(std::declval<_T&>().internal_foo.test(), std::declval<ReturnType>()) action() {
static_cast<T&>(*this).internal_foo.test();
}

// Way 2
template<class _T = T>
auto action() -> decltype(static_cast<_T&>(*this).internal_foo.test()) {
static_cast<_T&>(*this).internal_foo.test(); // Using _T for consistency
}

请注意,我使用 _Tdecltype里面所以它依赖于模板参数并且可以是 SFINAEd。另请注意,仍然可以在没有任何 enable_if 的情况下指定任意返回类型。

详情

我冒昧地在前面添加了#include <type_traits>using namespace std;到您的两个示例并使用 C++17,以便它们可以被编译。

评论区的一些发现:

  1. 您的第一个代码(未)按预期使用 Clang 14、gcc 11 和 gcc trunk 编译:https://godbolt.org/z/EbaYvfPE3
  2. 您的第二个代码(未)按预期使用 Clang add gcc trunk 进行编译,但 gcc 11 不同:https://godbolt.org/z/bbKrP8Mb9

有一个更容易复制的例子:https://godbolt.org/z/T17dG3Mx1

#include <type_traits>

template<class, class = void>
struct has_test : std::false_type {};

template<class T>
struct has_test<T, std::void_t<decltype(std::declval<T>().test())>> : std::true_type {};

class HasPrivateTest
{
public:
template<class, class>
friend struct has_test;
friend void foo();

private:
void test() {}
};

// Comment the following line to make it compile with GCC 11
static_assert(has_test<HasPrivateTest>::value, "");
void foo() {
static_assert(has_test<HasPrivateTest>::value, "");
}
static_assert(has_test<HasPrivateTest>::value, "");

上面的代码使用 Clang 14 和 gcc trunk 编译,但被 gcc 11 拒绝并显示三个“静态断言失败”消息,每个断言一个。但是,注释掉第一个 static_assert使所有三个编译器都接受代码。

所以看起来 GCC 11(及更早版本)尝试实例化模板并根据上下文进行访问检查。因此,如果第一个实例化是在 friend 之外,.test()方法不可访问,结果被缓存。但是,如果它在 friend void foo() 内, .test()可以访问并且所有static_assert我们成功了。

@Klaus 指出了最近的 GCC 错误,其修复似乎相关:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96204

关于C++ 使用 CRTP 检测 friend 类的私有(private)成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71795886/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com