Repository navigation
Open
Labels
clang-tidyfalse-positiveWarning fires when it should notWarning fires when it should notinvalid-code-generationTool (e.g. clang-format) produced invalid code that no longer compilesTool (e.g. clang-format) produced invalid code that no longer compiles
Description
The expressions std::tuple_size_v<T> and std::tuple_size<T>::value are not equivalent when used inside constraints (such as the requires expression or the definition of a concept). This is because std::tuple_size_v<T> is not SFINAE-friendly (the variable is always declared for every T). Consider the following code:
template <typename T>
bool check() {
if constexpr ( requires { std::tuple_size<T>::value; })
return true;
else
return false;
}Here, check<int>() returns false. Clang-tidy-23 suggests:
warning: use c++17 style variable templates [modernize-type-traits]
| if constexpr ( requires { std::tuple_size<T>::value; })
| ^ ~~~~~~~
| _v
However, if we replace std::tuple_size<T>::value with std::tuple_size_v<T>, check<int>() returns true.
The situation is even worse when we actually try to use the tuple size, e.g., in concept definitions. Consider the code:
template <typename T>
concept is_pair = std::tuple_size<T>::value == 2;
template <typename T>
bool check() {
return is_pair<T>;
}Now, check<int>() again returns false. However, if we follow clang-tidy's suggestion and use std::tuple_size_v<T>, calling check<int>() does not compile:
error: implicit instantiation of undefined template 'std::tuple_size<int>'
| inline constexpr size_t tuple_size_v = tuple_size<_Tp>::value;
| ^
note: in instantiation of variable template specialization 'std::tuple_size_v<int>' requested here
| concept is_pair = std::tuple_size_v<T> == 2;
| ^
note: while substituting template arguments into constraint expression here
| concept is_pair = std::tuple_size_v<T> == 2;
| ^~~~~~~~~~~~~~~~~~~~~~~~~
note: while checking the satisfaction of concept 'is_pair<int>' requested here
| return is_pair<T>;
| ^~~~~~~~~~
note: in instantiation of function template specialization 'check<int>' requested here
| return check<int>();
| ^
note: template is declared here
| struct tuple_size;
| ^
Metadata
Metadata
Assignees
Labels
clang-tidyfalse-positiveWarning fires when it should notWarning fires when it should notinvalid-code-generationTool (e.g. clang-format) produced invalid code that no longer compilesTool (e.g. clang-format) produced invalid code that no longer compiles
Activity
llvmorg-github-actions commentedon Oct 18, 2026
@llvm/issue-subscribers-clang-tidy
Author: Nikola Beneš (nikola-benes)
Details
The expressions `std::tuple_size_v<T>` and `std::tuple_size<T>::value` are not equivalent when used inside constraints (such as the `require` expression or the definition of a concept). This is because `std::tuple_size_v<T>` is not SFINAE-friendly (the variable is always declared for every T). Consider the following code:Here,
check<int>()returnsfalse. Clang-tidy-23 suggests:However, if we replace
std::tuple_size<T>::valuewithstd::tuple_size_v<T>,check<int>()returnstrue.The situation is even worse when we actually try to use the tuple size, e.g., in concept definitions. Consider the code:
Now,
check<int>again returnsfalse. However, if we follow clang-tidy's suggestion and usestd::tuple_size_v<T>, callingcheck<int>()does not compile:It seems that the same issue affects all the
_vvariable shortcuts. (The type_tshortcuts seem to be fine, however.)[-][clang-tidy] modernize-type-traits suggestion to use `_v` is wrong inside constraints[/-][+][clang-tidy] modernize-type-traits suggestion to use `tuple_size_v` is wrong inside constraints[/+][clang-tidy] Don't suggest tuple_size_v in constraints in type-traits
yashaggarwal85d commentedon Oct 24, 2026
I'd like to work on this.
std::tuple_size<T>::valuecan be a substitution failure whilestd::tuple_size_v<T>is a hard error, so they only behave differently where a substitution failure matters. Of the traits the check handles, onlytuple_sizeandvariant_sizehave a primary template that isn't defined for every argument, so the other_vsuggestions are still correct inside constraints. I'll send a PR that skipstuple_size/variant_sizewhen the dependent::valueis inside a requires-expression, a concept definition or a requires-clause.