Fedor
Fedor

Reputation: 21317

Can a function parameter be used as template argument?

In the following program, function foo receives parameter v of type std::integral_constant<bool, true>, which is used for passing in template argument A<b>:

template <bool>
struct A{};

constexpr bool foo(auto b) {
    return requires { typename A<b>; };
}

static_assert( foo( std::true_type{} ) );

Both GCC and MSVC find it valid (foo returns true), but in Clang foo returns false. Online demo: https://gcc.godbolt.org/z/j6Mnqjvbz

Which compiler is correct here?

Upvotes: 11

Views: 203

Answers (1)

Jan Schultke
Jan Schultke

Reputation: 39869

The code you have provided is correct. It fails because of a known LLVM bug 42406 / issue 41751.

You can call member functions on objects that don't exist in constant expressions, as long as the member functions don't access the object. Two classic examples:

std::array<int, 10> arr; // arr is not usable in a constant expression,
                         // but we can obtain its size()
static_assert(arr.size() == 10);

std::true_type t;
static_assert(t); // does not access t, but calls its operator bool()

Clang compiles both of the above. The same logic extends to function parameters, which are never usable in a constant expression:

// your code, except using std::true_type instead of auto
constexpr bool foo(std::true_type b) {
    return requires { typename A<b>; };
}

The code above should work, but results in a compiler error for clang:

error: reference to local variable 'b' declared in enclosing function > 'foo'
    7 |     return requires { typename A<b>; };
      |                                  ^
note: 'b' declared here
    6 | constexpr bool foo(std::true_type b) {
      | 

Your static assertion fails for the same reason, though you can understand the issue better when avoiding templates. This error message is obviously nonsensical and a compiler bug because no reference is being declared.

Upvotes: 2

Related Questions