I am not sure why the below code doesn't compile:
#include <array>
#include <algorithm>
#include <type_traits>
#include <concepts>
#include <cstdio>
#include <cwchar>
template <typename T>
concept Character = std::same_as< std::remove_cv_t<T>, char > ||
std::same_as< std::remove_cv_t<T>, signed char > ||
std::same_as< std::remove_cv_t<T>, unsigned char > ||
std::same_as< std::remove_cv_t<T>, wchar_t > ||
std::same_as< std::remove_cv_t<T>, char8_t > ||
std::same_as< std::remove_cv_t<T>, char16_t > ||
std::same_as< std::remove_cv_t<T>, char32_t >;
template <std::size_t N>
consteval auto create_character_array( std::integral auto&& fill_character ) noexcept
{
std::array<std::remove_cvref_t<decltype( fill_character )>, N> dashes { };
std::fill( std::begin( dashes ), std::end( dashes ), fill_character );
return dashes;
}
int main( )
{
const char ch { '^' };
auto dashes { create_character_array<128>( ch ) };
dashes.back( ) = '\0';
std::fputs( std::data( dashes ), stdout );
}
I'm not sure but it seems that the universal reference (auto&&
) and the concept std::integral
don't work well for some reason. And replacing std::integral
with my own more precise concept Character
doesn't help either.
The following error is raised:
<source>: In function 'int main()':
<source>:30:46: error: no matching function for call to 'create_character_array<128>(const char&)'
30 | auto dashes { create_character_array<128>( ch ) };
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
<source>:19:16: note: candidate: 'template<long unsigned int N, class auto:16> requires integral<auto:16> consteval auto create_character_array(auto:16&&)'
19 | consteval auto create_character_array( std::integral auto&& fill_character ) noexcept
| ^~~~~~~~~~~~~~~~~~~~~~
<source>:19:16: note: template argument deduction/substitution failed:
<source>:19:16: note: constraints not satisfied
In file included from /opt/compiler-explorer/gcc-trunk-20230509/include/c++/14.0.0/compare:37,
from /opt/compiler-explorer/gcc-trunk-20230509/include/c++/14.0.0/array:38,
from <source>:1:
/opt/compiler-explorer/gcc-trunk-20230509/include/c++/14.0.0/concepts: In substitution of 'template<long unsigned int N, class auto:16> requires integral<auto:16> consteval auto create_character_array(auto:16&&) [with long unsigned int N = 128; auto:16 = const char&]':
<source>:30:46: required from here
/opt/compiler-explorer/gcc-trunk-20230509/include/c++/14.0.0/concepts:100:13: required for the satisfaction of 'integral<auto:16>' [with auto:16 = const char&]
/opt/compiler-explorer/gcc-trunk-20230509/include/c++/14.0.0/concepts:100:24: note: the expression 'is_integral_v<_Tp> [with _Tp = const char&]' evaluated to 'false'
100 | concept integral = is_integral_v<_Tp>;
| ^~~~~~~~~~~~~~~~~~
ASM generation compiler returned: 1
<source>: In function 'int main()':
<source>:30:46: error: no matching function for call to 'create_character_array<128>(const char&)'
30 | auto dashes { create_character_array<128>( ch ) };
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
<source>:19:16: note: candidate: 'template<long unsigned int N, class auto:16> requires integral<auto:16> consteval auto create_character_array(auto:16&&)'
19 | consteval auto create_character_array( std::integral auto&& fill_character ) noexcept
| ^~~~~~~~~~~~~~~~~~~~~~
<source>:19:16: note: template argument deduction/substitution failed:
<source>:19:16: note: constraints not satisfied
In file included from /opt/compiler-explorer/gcc-trunk-20230509/include/c++/14.0.0/compare:37,
from /opt/compiler-explorer/gcc-trunk-20230509/include/c++/14.0.0/array:38,
from <source>:1:
/opt/compiler-explorer/gcc-trunk-20230509/include/c++/14.0.0/concepts: In substitution of 'template<long unsigned int N, class auto:16> requires integral<auto:16> consteval auto create_character_array(auto:16&&) [with long unsigned int N = 128; auto:16 = const char&]':
<source>:30:46: required from here
/opt/compiler-explorer/gcc-trunk-20230509/include/c++/14.0.0/concepts:100:13: required for the satisfaction of 'integral<auto:16>' [with auto:16 = const char&]
/opt/compiler-explorer/gcc-trunk-20230509/include/c++/14.0.0/concepts:100:24: note: the expression 'is_integral_v<_Tp> [with _Tp = const char&]' evaluated to 'false'
100 | concept integral = is_integral_v<_Tp>;
| ^~~~~~~~~~~~~~~~~~
It compiles when '^'
is passed as the argument. However ch
makes it fail. This happened after adding std::integral
to the function definition. Without it, the code compiles.
And in case this is not a good design, how should I define the above function to make it as versatile as possible? I want it to accept any value category.
The definition auto func(std::integral auto&& arg)
is just shorthand for
template <typename T>
requires std::integral<T>
auto func(T&& arg)
The way forwarding references work is that when you call func
, T
will be deduced to either a value type if the argument is an rvalue or a reference type if the argument is an lvalue:
func(42); // T is int, so the type of arg is int&&
func(some_int); // T is int&, so the type of arg is int& &&, which colapses to int&
In the latter case, the std::integral<T>
constraint is unsatisfied, since int&
is not an integral type.
To avoid this issue, either drop the reference (integral types are cheap to copy):
template <std::size_t N>
consteval auto create_character_array( std::integral auto fill_character ) noexcept
Or spell out the requirement long-form and explicitly add a std::remove_reference_t
:
template <std::size_t N, typename T>
requires std::integral<std::remove_reference_t<T>>
consteval auto create_character_array( T&& fill_character ) noexcept