c++language-lawyerconstexpr

Should this example for non structural constexpr C++ argument compile?


I have the following code from Ben Deane talk.

#define CX_VALUE(...) [] { \
    struct { \
        constexpr auto operator()() const noexcept { return __VA_ARGS__; } \
        using cx_value_tag = void; \
    } val; \
    return val; \
}()

template <typename T>
concept cx_value = requires { typename T::cx_value_tag; };

auto func(cx_value auto x) { 
    constexpr auto val = x();
    std::cout << "Constexpr value: " << val.val() << '\n';
}

class S {
    public:
    constexpr S(const int val) :  val_(val){}
    int val() const {
        return val_;
    }
    private:
    int val_;
};

int main() {
    constexpr S non_structural_value{420};
    func(CX_VALUE(non_structural_value));
}

Issue is that clang rejects it, gcc accepts it.

Which compiler is right(or is it maybe underspecified in the standard)?


Solution

  • This has nothing to do with the properties of the class S.

    The problem can be reduced to

    struct S {};
    int main() {
        constexpr S s{};
        []{
            struct {
                constexpr auto operator()() { return s; }
            } val;
        };
    }
    

    The problem is that s is being odr-used here in return s; because it doesn't have the lvalue-to-rvalue conversion applied to it (because it is of class type). And s is not odr-usable in this scope.

    For the same reason you would need to capture s if you tried to write []{ return s; } instead. But in the case above capturing won't help either, s will still not be odr-usable in the scope of the local class.

    Clang is correct.