最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - How to check if a class has a protected member function? - Stack Overflow

programmeradmin2浏览0评论

Consider this simple piece of C++ code:

class Foo
{
  public:
    Foo() = default;
};

int main() {
    static_assert(std::is_default_constructible_v<Foo>);

    return 0;
}

Foo has public default constructor, so static_assert passes. On the other hand, if public is changed to private, Foo can't be default-constructed, so static_assert does not pass. So far, so good.

Now, if public is changed to protected, Foo is still not default-constructible from main, so static_assert again does not pass. However, if some class Bar was to inherit from Foo, Foo would be default-constructible from within Bar but static_assert would still not pass. This is sensible, but problematic.

Now, my question is: is it possible (and how) to construct class Bar that inherits from Foo, has some important default-construction logic, and is default-constructible only if it can default-construct Foo.

In other words, can Bar be made so that the following code passes:

#include <type_traits>

class Foo
{
  protected:
    Foo() = default;
};

class Doo
{
  protected:
    Doo() = delete;
};

template <typename T>
struct Bar : public T
{
    // some implementation ...
    Bar()
    // some magical `requires` statement here ...
    : T() 
    {
        // some very important logic here!
    };
};

int main() {
    static_assert(std::is_default_constructible_v<Bar<Foo>>);
    static_assert(!std::is_default_constructible_v<Bar<Doo>>);

    return 0;
}

NOTE1: If protected is replaced with public in the preceding code, the solution would be simple: constructor of Bar would just have to require T to be default-constructible. However, it is very important that Foo and Doo have protected constructors since they are never to be used on their own.

NOTE2: This problem has nothing to do with constructors, it can be about any member function. I picked default-constructor solely because there is an existing type trait std::is_default_constructible_v.

Consider this simple piece of C++ code:

class Foo
{
  public:
    Foo() = default;
};

int main() {
    static_assert(std::is_default_constructible_v<Foo>);

    return 0;
}

Foo has public default constructor, so static_assert passes. On the other hand, if public is changed to private, Foo can't be default-constructed, so static_assert does not pass. So far, so good.

Now, if public is changed to protected, Foo is still not default-constructible from main, so static_assert again does not pass. However, if some class Bar was to inherit from Foo, Foo would be default-constructible from within Bar but static_assert would still not pass. This is sensible, but problematic.

Now, my question is: is it possible (and how) to construct class Bar that inherits from Foo, has some important default-construction logic, and is default-constructible only if it can default-construct Foo.

In other words, can Bar be made so that the following code passes:

#include <type_traits>

class Foo
{
  protected:
    Foo() = default;
};

class Doo
{
  protected:
    Doo() = delete;
};

template <typename T>
struct Bar : public T
{
    // some implementation ...
    Bar()
    // some magical `requires` statement here ...
    : T() 
    {
        // some very important logic here!
    };
};

int main() {
    static_assert(std::is_default_constructible_v<Bar<Foo>>);
    static_assert(!std::is_default_constructible_v<Bar<Doo>>);

    return 0;
}

NOTE1: If protected is replaced with public in the preceding code, the solution would be simple: constructor of Bar would just have to require T to be default-constructible. However, it is very important that Foo and Doo have protected constructors since they are never to be used on their own.

NOTE2: This problem has nothing to do with constructors, it can be about any member function. I picked default-constructor solely because there is an existing type trait std::is_default_constructible_v.

Share Improve this question asked Feb 16 at 13:35 lobelklobelk 4962 silver badges11 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 2

Using = default; would allow to "propagate" the possibility to construct the class:

template <typename T>
struct Bar : public T
{
    Bar() = default;
};

static_assert(std::is_default_constructible_v<Bar<Foo>>);
static_assert(!std::is_default_constructible_v<Bar<Doo>>);

If you want to apply it to another derived, you might use the traits on Bar<T> instead.

And for regular method:

template <typename T>
struct Bar : public T
{
    template <typename U = T>
    auto foo() -> decltype(U::foo()) {}
};

template <typename T>
concept has_foo = requires(T t) { t.foo(); };
static_assert(has_foo<Bar<Foo>>);
static_assert(!has_foo<Bar<Doo>>);
发布评论

评论列表(0)

  1. 暂无评论