2

Consider the following concept:

template< typename T, std::size_t Value >
concept Is_ = requires(T&&)
{
    requires Value == std::remove_cvref_t< T >::value;
};

Associated with the function overloads:

template< Is_< 2 > T >
void foo(T&&)
{
    std::cout << "Value is 2!" << std::endl;
}

template< typename T >
void foo(T&&)
{
    std::cout << "Either no value or it isn't 2!" << std::endl;
}

Finally, let's play with the above functions:

foo(std::integral_constant< std::size_t, 2 >{}); // Choose the constrained overload
foo(std::tuple{}) // Choose the generic impl

Now if I define the following helper variable template:

template< typename T >
inline constexpr auto t_value = T::value;

And use it in the concept for brevity's sake:

template< typename T, std::size_t Value >
concept Is_ = requires(T&&)
{
    requires Value == t_value< std::remove_cvref_t< T > >;
};

I now get this compiler error:

main.cpp: In instantiation of 'constexpr const auto t_value':

main.cpp:11:23: required by substitution of 'template requires Is_<T, 2> void foo(T&&) [with T = int]'

main.cpp:27:8: required from here

main.cpp:6:29: error: 'value' is not a member of 'int'

6 | constexpr auto t_value = T::value;

Having invalid type aliases is however perfectly fine.

MVCE is available here: https://coliru.stacked-crooked.com/a/653bb52448ca1db3

Where does this behavior come from?

Rerito
  • 5,886
  • 21
  • 47
  • If I uncomment the line marked with error this still works fine. (gcc13.1) Maybe your compiler haven't implemented everything from c++20? – Botond Horváth Jul 11 '23 at 10:41
  • 1
    Great example of a MCVE that does not match the code in the question. – Nelfeal Jul 11 '23 at 10:41
  • 1
    Removing the type check reproduces the error. I edited the link in the question to reflect that. – Rerito Jul 11 '23 at 10:43
  • 1
    See also [Why does GCC succeeds on a constraint with std::tuple_size::value, but fails on one with std::tuple_size_v?](https://stackoverflow.com/q/68143642/636019) – ildjarn Jul 13 '23 at 10:12
  • Oh that's absolutely on point @ildjarn thank you (you might even say that my question is a dupe of the linked one, it's just that I couldn't find it with my wording) – Rerito Jul 17 '23 at 13:49
  • @Rerito : I came across it from personal research ~a month ago, total coincidence :-D – ildjarn Jul 18 '23 at 06:00

1 Answers1

4

It happens because the instantiation of the variable template is not in the "immediate context" of the concept when it is being substituted and verified. Invalid types and expressions are only (and always have been) sfinae-friendly in the immediate context of the template argument substitution where they appear.

And since t_value is entirely different template, any failure that would occur during its instantiation would not be a "soft failure" in a concept that uses one of its specializations.


By the way, to address the original link in your post. Alias templates have special provisions that oftentimes allow them to be a drop-in replacement where whatever they are aliasing would appear (think how std::enable_if_t is also sfinae friendly, and always has been). When you checked the t_value_type alias in the conecpt before the value, it already failed in a sfinae-friendly way, so the concept evaluated to false (since the constraints were checked in order of appearance).

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458