Search code examples
c++templatesc++11constexprargument-dependent-lookup

Getting constexpr functions resolved without creating parameter objects


Short version:

If I have function like:

constexpr bool has_some_property(Foo) { return true; }

Is there any way to invoke the function without having to actually instantiate Foo? Say if Foo is not default constructible?

Long winded version:

Anthony Williams recently wrote an article detailing a set of free functions enabled for any enum class object that specialized a specific template. It follows a similar scheme in <ios>, std::is_error_code, where one specializes the template for a user defined type or value to allow enable_if to enable some functions. In Anthony's case:

template<>
struct enable_bitmask_operators<my_bitmask>{
    static constexpr bool enable=true;
};

And then when operators are defined:

template<typename E>
typename std::enable_if<enable_bitmask_operators<E>::enable,E>::type
operator|(E lhs,E rhs){

The problem with this technique is that the template specialization has to be in the same namespace as the original template, so this doesn't work:

namespace mystuff {
    enum class Foo {
        ...
    };

    // Fail: wrong namespace
    template<>
    struct enable_bitmask_operators<Foo> : std::true_type {}

An alternative is to use a constexpr function, which can be resolved in the same namespace as the class:

namespace mystuff {
    enum class Foo {
        ...
    };
    constexpr bool enable_bitmask_operators(Foo) { return true; }

And then at definition:

template<typename E>
typename std::enable_if<enable_bitmask_operators(E()),E>::type
operator|(E lhs,E rhs){

The nice thing about this is that it work nicely even with nested classes. The problem with it is that it requires a default constructible class. That works fine for our enum class example, but it doesn't work as a general solution to the issue of specialization. So if we imagine trying to use a constexpr function instead of a template specialization for some other class we could get other failures:

struct Foo {
    Foo() = delete;
};
constexpr bool has_some_property(Foo) { return true; }

...

// Fail for Foo...use of deleted function
template<typename E>
typename std::enable_if<has_some_property(E()),E>::type doStuff() {}

Its a little frustrating because I don't actually need that object to be created, I just want it there for ADL to identify the constexpr function to call. I keep thinking there should be some way I can say that I want that function without having to actually create the object. I've played around with std::declval but that doesn't work in this case.

Does anybody see a way around this quandary?


Solution

  • Just don't use constexpr:

    std::true_type has_some_property(Foo&& );
    

    Does Foo have it?

    using has_it = decltype(has_some_property(std::declval<Foo>()));
    static_assert(has_it::value, "It should!");
    

    This is an unevaluated context, so we never have to call any Foo constructor. And can sidestep the issues with constexpr of requiring constant expressions.