Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to forward argument to inner constexpr function?

The question: is it possible to evaluate constant expression inside a function by passing (maybe with some kind of "perfect forwarding") its argument to inner constexpr function? Example:

constexpr size_t foo(char const* string_literal) {
    return /*some valid recursive black magic*/;
}

void bar(char const* string_literal) {
    // works fine
    constexpr auto a = foo("Definitely string literal.");
    // compile error: "string_literal" is not a constant expression
    constexpr auto b = foo(string_literal);
}

template<typename T>
void baz(T&& string_literal) {
    // doesn't compile as well with the same error
    constexpr auto b = foo(std::forward<T>(string_literal));
}

int main() {
    // gonna do this, wont compile due to errors mentioned above
    bar("Definitely string literal too!");
}

Can't find anything clearly prohibiting in the documentation, but the solution isn't found, as well as a proof of impossibility. Constexpr'ness of inner expression is important.

like image 240
Trollliar Avatar asked Aug 30 '16 20:08

Trollliar


2 Answers

Parameters to constexpr functions cannot be assumed to be constexpr within a constexpr function; the function must work if they are not constexpr.

Type parameters can be.

If you replaced bar("hello") with bar( string_constant<'h', 'e', 'l', 'l', 'o'>{} ) with template<char...>struct string_constant{};, the value of the characters is now encoded in the type and will be available down the path. There are other ways to get the characters into a type.

like image 84
Yakk - Adam Nevraumont Avatar answered Oct 24 '22 01:10

Yakk - Adam Nevraumont


Unfortunately, this cannot be achieved. Parameters to a constexpr function are not automatically constexpr as well. The function can after all be called from non-constexpr context. Your compiler might be able to optimize your case into compile-time evaluation, but this is in no way guaranteed.

There is a standing workaround to use template parameters to force a kind of constexpr-ness to a parameter. You can find a good example in this question. Following on that, one might be tempted to do this:

template<const char * string_literal> void baz() {
    constexpr auto b = foo(string_literal); 
}

int main() {
    baz<"Definitely string literal too!">(); 
}

However, this comes with the limitations on non-type template parameters, one of which says that a string literal cannot be a non-type template parameter. You can use the variadic char template, as suggested by Yakk, if that can be applied to your case.

The support for constexpr function parameters might be added in the future. There is a discussion on ISO C++ Google Groups here.

You can also convert baz into some kind of parameterized macro, if you really need the job done.

like image 3
Jakub Zaverka Avatar answered Oct 24 '22 02:10

Jakub Zaverka