Using constant expressions as template parameter using a function parameter: which compiler is right?
Asked Answered
P

1

6

I have this piece of code right there:

template <int V>
struct Constant {
    constexpr operator int() const noexcept { return V; }
};


template <class T, int N>
struct Array { };

auto function(auto s) -> Array<int, s + s> {
    return {};
}

auto const a = function(Constant<3>{});

To my greatest sadness, only Clang seems to accept this code.

Which compiler is right, and why?

Pachysandra answered 27/2, 2024 at 3:53 Comment(6)
Looks like clang decays the std::integral_constant into the constant and then does the addition while gcc and MSVS just try to add the integer_constant which doesn't have an operator + so the template parameter is invalid. Not sure who is right here but I feel it is gcc and MSVS since conversions are not supposed to be done in template argument deduction.Cide
@user12002570 you are wrong. This question is a good one. I know exactly what I'm asking for. I will probably use the result of this question for reporting actual compiler bugs so they agree with each other if s decays to int at compile time or not.Pachysandra
@GuillaumeRacicot " if s decays to int at compile time or not. " That is the question.Stationery
@Stationery I think I asked this right. s + s is indeed a constant expression. You can totally use std::array<int, s + s> in the body of the function, and all compiler accepts it: godbolt.org/z/jdMd9aoWbPachysandra
interestingly MSVS will compile if the function is adjusted to take in a deduced integral_constant but uses that integral_constant to get the array size: godbolt.org/z/d7Tarz9f5. gcc still wont compile unless you use the deduced size: godbolt.org/z/6jo78xGqhCide
@Cide clang seems to accept std::array<int, s> in the trailing return type too. I would actually expect clang to be right since all compiler accepts the expression inside the body of the function: godbolt.org/z/czs1MKs6sPachysandra
S
5

I believe clang is correct here. Opened #114124 for gcc.

Note that both gcc and MSVC accept this equivalent formulation:

auto function(auto s) {
    return Array<int, s+s>{};
}

It's just that they don't allow the use of the parameter in the return type.

But in the same way that writing s+s as a constant expression in the body of the function doesn't violate any rules of [expr.const]/5, no rules are violated in the trailing-return-type. We're just as much not reading anything (i.e. the usual reason such programs fail is because we're attempting an lvalue-to-rvalue conversion on a value not known to the constant evaluator), since the conversion function to int is constexpr and doesn't rely on any data.

Sextuplet answered 27/2, 2024 at 4:17 Comment(10)
I opened this one for MSVC, I think it should cover it: developercommunity.visualstudio.com/t/…Pachysandra
@Stationery That program is not ill formed and a ill formed program is not a valid reason to close a question anyway.Pachysandra
@Stationery It is not an intuitition-based answer. I specifically noted that we're not violating any rules in [expr.const], which are the relevant ones here - it's a long list of things you are not allowed to do in a constant expression, none of which we're doing. It would be not be helpful to enumerate every rule we're not breaking in this program.Sextuplet
@Stationery this explains why my program is actually valid: https://mcmap.net/q/539419/-integral-constant-passed-by-value-treated-as-constexpr the standardese is written there. The fact that you thought it was an ill-formed program was the intuition based conclusion. Barry answer is satisfactory enough for me.Pachysandra
as most comments seem to be deleted but the initial claim "Function parameter are never constexpr" persists, could you clarify if this statement is wrong or not? I am confused, because I have read it so often beforeLonni
@Lonni nothing of value was lost in those deleted comments. Function parameters cannot be used to initiate a compile time evaluation or nested compile time evaluation since they are not themselves marked as constexpr. In my example, despite it seems like I'm using s in s + s, the expression s is not evaluated and a new compile time evaluation is initiated.Pachysandra
@GuillaumeRacicot because Array<T,N> does not actually use N? Thats what you mean with "... is not evaluated .... " ?Lonni
@Lonni in the evaluation of Array<into, s>, in the case where s is an integral constant, s is not being evaluated since the compiler decays s into an integral value, and the conversion operator is constexpr and do not dereference this, so it is evaluable at compile time.Pachysandra
@Lonni The statement is true: function parameters are not constexpr. However in this case, we're not reading the value of the parameter - we're using its (stateless) constexpr conversion function to int.Sextuplet
@Sextuplet that made it click. Breaking it down to the trivial, it is similar to calling a const method to get a const value returned, you can do that on a non-const instance. s isnt constexpr but thats not that relevantLonni

© 2022 - 2025 — McMap. All rights reserved.