简体   繁体   中英

How to statically query a static member variable that may not exist, providing a default?

I'm programming some classes into which I inject dependencies via class template parameters.

I some cases, the dependency classes have, or can have, static constexpr members that declare some of their specific characteristics. In the case of the example below, classes implementing a Renderer "concept" can define a static constexpr bool variable y_axis_bottom_up to indicate that they expect vertical coordinates that grow in upward direction.

I could of course make it a requirement that all Renderer implementations provide this boolean member, but I'd prefer to define a default value in the query expression.

Because I need to use that information to parameterize further templates, that query expression must be constexpr.

I thought I had the solution when I found this stackoverflow answer , but when I tried to apply it to my needs, I failed.

Here's the - non-working - minimal test code I came up with. The second output line should say "1", but both outputs are "0".

Any help would be appreciated.

#include <iostream>
#include <type_traits>

template<class Config, typename = void>
struct vertical_axis_bottom_up
{
    static constexpr bool value = false;
};

template<class Config> 
struct vertical_axis_bottom_up<Config, decltype(Config::Renderer::y_axis_bottom_up)>
{
    static constexpr bool value = Config::Renderer::y_axis_bottom_up;
};

struct Dummy_renderer {};

struct Config1
{
    using Renderer = Dummy_renderer;
};

struct Dummy_renderer_2
{
    static constexpr bool y_axis_bottom_up = true;
};

struct Config2
{
    using Renderer = Dummy_renderer_2;
};

int main(int /*argc*/, char * /*argv*/[])
{
    std::cout << "Default value for Config::Renderer::y_axis_bottom_up : " << vertical_axis_bottom_up<Config1>::value << std::endl;
    std::cout << "Explicit value for Config::Renderer::y_axis_bottom_up: " << vertical_axis_bottom_up<Config2>::value << std::endl;

    std::cout << "Press RETURN to terminate" << std::endl;
    char ch; std::cin >> std::noskipws >> ch;

    return -1;
}

This is done extensively in the standard library, allocator_traits is a good example. Here's the basic idea:

#include <iostream>
#include <type_traits>


template <class T, class = void>
struct MyTrait
{
    static constexpr bool foo = false;
};

template <class T>
struct MyTrait<T, std::enable_if_t<std::is_same<const bool, decltype(T::foo)>::value>>
{
    static constexpr bool foo = T::foo;
};

struct Class1 {

};

struct Class2 {
 static constexpr bool foo = true;   
};


int main()
{
    std::cerr << MyTrait<Class1>::foo;
    std::cerr << MyTrait<Class2>::foo;
}

Basically, this is using a trick very similar to the void_t trick (google if you are not familiar). If the templated class provides a member of the correct type, then it picks up that member. If not, it falls back to false. So when you access the foo trait through MyTrait , you are guaranteed for it be present.

Working example: http://coliru.stacked-crooked.com/a/d73f3674d0c53e53

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM