3

I have this snippet.

#include <iostream>
#include <string>

struct JustStr {
    JustStr(const std::string& x) : val(x) {}
    static constexpr bool pred = false;
    std::string val;
};

template <typename T>
class C {
 private:
    T x;
 public:
    C(T x_) : x(x_) {}
    void f() {
        if constexpr (!x.pred) {
                std::cout << x.val << std::endl;
            }
    }

};

template<typename T>
void f2(T x) {
    T y(x);
    if constexpr (!y.pred) {
            std::cout << x.val << std::endl;
        }
}

int main() {
    C<JustStr> c(JustStr("yes"));
    c.f();  // Fails
    f2(JustStr("yes"));  // Succeeds
    return 0;
}

My question is: shouldn't c.f(); and f2(JustStr("yes")); fail or succeed simultaneously? Why does the one fail and why does the other succeed?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
fakedrake
  • 6,528
  • 8
  • 41
  • 64

1 Answers1

6

There is a list of things that prevent an expression from being considered as a core constant expression.

One of the things you cannot do is evaluate:

this, except in a constexpr function or a constexpr constructor that is being evaluated as part of e;

In f(), we're evaluating this in order to see what x.pred is (because really it's this->x.pred), but f() isn't a constexpr function. So this bullet point rules out c.f(). If f() were constexpr, then this would compile.

In f2(), we're not evaluating this anywhere, so that bullet doesn't apply. We do an lvalue-to-rvalue conversion, but it does refer to a complete non-volatile const object with a preceding initialization, initialized with a constant expression. Nothing else applies. So it's okay.

Barry
  • 286,269
  • 29
  • 621
  • 977