如何将 void 模板类型用于编译时条件代码和 static_assert?
How can I use void template type for compile-time conditional code and static_assert?
我有遍历函数,它为每个元素调用回调,并希望在编译时使用非默认参数实现它的逻辑扩展,如下所示:
#include <type_traits>
#include <iostream>
using namespace std;
template<
typename Odds = void,
typename Evens = void,
typename Skip = void
>
void iterate(
const auto & on_element,
const auto & skip
) {
for ( int i = 0; i < 6; ++ i ) {
if constexpr ( ! is_same_v< Skip, void > ) {
if ( skip.find( i ) != skip.end() )
continue;
}
if ( i % 2 == 1 ) {
if constexpr ( is_same_v< Odds, void > )
on_element( i );
}
else {
if constexpr ( is_same_v< Evens, void > )
on_element( i );
}
static_assert(
is_same_v< Odds, void > || is_same_v< Evens, void >,
"Either odds or evens are required to get traversed"
);
}
}
int main() {
const auto & on_element = []( const int & i ) {
cout << " " << i << endl;
};
cout << "Whole range:" << endl;
iterate( on_element );
cout << "Should skip specified elements:" << endl;
iterate( on_element, { 1, 2, 3 } );
cout << "Should traverse only evens:" << endl;
iterate< bool >( on_element );
cout << "Should traverse only odds:" << endl;
iterate< void, bool >( on_element );
//should NOT compile if uncomment:
//iterate< bool, bool >( on_element );
}
但它不能用g++ -std=gnu++2a -fconcepts main.cpp -o main
编译:
main.cpp: In function ‘int main()’:
main.cpp:42:25: error: no matching function for call to ‘iterate(const main()::<lambda(const int&)>&)’
42 | iterate( on_element );
| ^
main.cpp:10:6: note: candidate: ‘template<class Odds, class Evens, class Skip, class auto:11, class auto:12> void iterate(const auto:11&, const auto:12&)’
10 | void iterate(
| ^~~~~~~
main.cpp:10:6: note: template argument deduction/substitution failed:
main.cpp:42:25: note: candidate expects 2 arguments, 1 provided
42 | iterate( on_element );
| ^
main.cpp:45:38: error: no matching function for call to ‘iterate(const main()::<lambda(const int&)>&, <brace-enclosed initializer list>)’
45 | iterate( on_element, { 1, 2, 3 } );
| ^
main.cpp:10:6: note: candidate: ‘template<class Odds, class Evens, class Skip, class auto:11, class auto:12> void iterate(const auto:11&, const auto:12&)’
10 | void iterate(
| ^~~~~~~
main.cpp:10:6: note: template argument deduction/substitution failed:
main.cpp:45:38: note: couldn’t deduce template parameter ‘auto:12’
45 | iterate( on_element, { 1, 2, 3 } );
| ^
main.cpp:48:33: error: no matching function for call to ‘iterate<bool>(const main()::<lambda(const int&)>&)’
48 | iterate< bool >( on_element );
| ^
main.cpp:10:6: note: candidate: ‘template<class Odds, class Evens, class Skip, class auto:11, class auto:12> void iterate(const auto:11&, const auto:12&)’
10 | void iterate(
| ^~~~~~~
main.cpp:10:6: note: template argument deduction/substitution failed:
main.cpp:48:33: note: candidate expects 2 arguments, 1 provided
48 | iterate< bool >( on_element );
| ^
main.cpp:51:39: error: no matching function for call to ‘iterate<void, bool>(const main()::<lambda(const int&)>&)’
51 | iterate< void, bool >( on_element );
| ^
main.cpp:10:6: note: candidate: ‘template<class Odds, class Evens, class Skip, class auto:11, class auto:12> void iterate(const auto:11&, const auto:12&)’
10 | void iterate(
| ^~~~~~~
main.cpp:10:6: note: template argument deduction/substitution failed:
main.cpp:51:39: note: candidate expects 2 arguments, 1 provided
51 | iterate< void, bool >( on_element );
| ^
有没有办法用 C++ 实现它?
也许有更好的编译时技术来实现所需的行为?
如果你需要iterate
的一个版本来接受一个参数,你必须实现一个。
在我看来,在这种情况下,您应该避免 auto
类型参数。
我提出以下代码
#include <type_traits>
#include <iostream>
#include <set>
using namespace std;
template<
bool Odds = true,
bool Evens = true,
typename Func,
typename Skip = bool
>
void iterate(
Func const & on_element,
Skip const & skip = false
) {
for ( int i = 0; i < 6; ++ i ) {
if constexpr ( ! is_same_v< Skip, bool > ) {
if ( skip.find( i ) != skip.end() )
continue;
}
if ( i % 2 == 1 ) {
if constexpr ( Odds )
on_element( i );
}
else {
if constexpr ( Evens )
on_element( i );
}
static_assert(
Odds || Evens,
"Either odds or evens are required to get traversed"
);
}
}
int main() {
const auto & on_element = []( const int & i ) {
cout << " " << i << endl;
};
cout << "Whole range:" << endl;
iterate( on_element );
cout << "Should skip specified elements:" << endl;
iterate( on_element, std::set{ 1, 2, 3 } );
cout << "Should traverse only evens:" << endl;
iterate< false >( on_element );
cout << "Should traverse only odds:" << endl;
iterate< true, false >( on_element );
// should NOT compile if uncomment:
// iterate< false, false >( on_element );
}
template<
bool Odds = false,
bool Evens = false,
bool Skip = false // or really, true: why pass skip if we won't use it?
>
void iterate(
const auto & on_element,
const auto & skip
)
并编写另一个重载:
template<
bool Odds = false,
bool Evens = false
> // no bool Skip; lack of arg is enough
void iterate(
const auto & on_element
)
{
iterate<Odds, Evens, false>(on_element);
}
然后将 ! is_same_v< Skip, void >
替换为 Skip
并类似替换 odds/evens。
还有
static_assert( !Skip||!std::is_same_v<decltype(skip), const int&>, "You must pass a 2nd argument" );
对生活质量有好处。
或许可以简化
#include <type_traits>
#include <iostream>
#include <algorithm>
#include <array>
auto Odds = [](auto&& i) { return i % 2 == 0; };
auto Evens = [](auto&& i) { return i % 2 == 1; };
auto None = [](auto&&) { return false; };
template<class...Ts>
struct These
{
constexpr These(Ts... args)
: args_ { args... }
{}
template<class Arg>
bool operator()(Arg&& i) const
{
return std::any_of(
std::begin(args_),
std::end(args_),
[i](auto x) { return x == i; }
);
}
std::array<std::common_type_t<Ts...>, sizeof...(Ts)> args_;
};
template<
typename Skip = decltype(None)
>
void iterate(
const auto & on_element,
Skip&& skip = Skip()
) {
for ( int i = 0; i < 6; ++ i ) {
if (skip(i))
continue;
std::cout << i << ", ";
}
std::cout << std::endl;
}
int main() {
const auto & on_element = []( const int & i ) {
std::cout << " " << i << std::endl;
};
std::cout << "Whole range:" << std::endl;
iterate( on_element );
std::cout << "Should skip specified elements:" << std::endl;
iterate( on_element, These( 1, 2, 3 ));
std::cout << "Should traverse only evens:" << std::endl;
iterate( on_element , Evens);
std::cout << "Should traverse only odds:" << std::endl;
iterate( on_element, Odds );
}
预期输出:
Whole range:
0, 1, 2, 3, 4, 5,
Should skip specified elements:
0, 4, 5,
Should traverse only evens:
0, 2, 4,
Should traverse only odds:
1, 3, 5,
我有遍历函数,它为每个元素调用回调,并希望在编译时使用非默认参数实现它的逻辑扩展,如下所示:
#include <type_traits>
#include <iostream>
using namespace std;
template<
typename Odds = void,
typename Evens = void,
typename Skip = void
>
void iterate(
const auto & on_element,
const auto & skip
) {
for ( int i = 0; i < 6; ++ i ) {
if constexpr ( ! is_same_v< Skip, void > ) {
if ( skip.find( i ) != skip.end() )
continue;
}
if ( i % 2 == 1 ) {
if constexpr ( is_same_v< Odds, void > )
on_element( i );
}
else {
if constexpr ( is_same_v< Evens, void > )
on_element( i );
}
static_assert(
is_same_v< Odds, void > || is_same_v< Evens, void >,
"Either odds or evens are required to get traversed"
);
}
}
int main() {
const auto & on_element = []( const int & i ) {
cout << " " << i << endl;
};
cout << "Whole range:" << endl;
iterate( on_element );
cout << "Should skip specified elements:" << endl;
iterate( on_element, { 1, 2, 3 } );
cout << "Should traverse only evens:" << endl;
iterate< bool >( on_element );
cout << "Should traverse only odds:" << endl;
iterate< void, bool >( on_element );
//should NOT compile if uncomment:
//iterate< bool, bool >( on_element );
}
但它不能用g++ -std=gnu++2a -fconcepts main.cpp -o main
编译:
main.cpp: In function ‘int main()’:
main.cpp:42:25: error: no matching function for call to ‘iterate(const main()::<lambda(const int&)>&)’
42 | iterate( on_element );
| ^
main.cpp:10:6: note: candidate: ‘template<class Odds, class Evens, class Skip, class auto:11, class auto:12> void iterate(const auto:11&, const auto:12&)’
10 | void iterate(
| ^~~~~~~
main.cpp:10:6: note: template argument deduction/substitution failed:
main.cpp:42:25: note: candidate expects 2 arguments, 1 provided
42 | iterate( on_element );
| ^
main.cpp:45:38: error: no matching function for call to ‘iterate(const main()::<lambda(const int&)>&, <brace-enclosed initializer list>)’
45 | iterate( on_element, { 1, 2, 3 } );
| ^
main.cpp:10:6: note: candidate: ‘template<class Odds, class Evens, class Skip, class auto:11, class auto:12> void iterate(const auto:11&, const auto:12&)’
10 | void iterate(
| ^~~~~~~
main.cpp:10:6: note: template argument deduction/substitution failed:
main.cpp:45:38: note: couldn’t deduce template parameter ‘auto:12’
45 | iterate( on_element, { 1, 2, 3 } );
| ^
main.cpp:48:33: error: no matching function for call to ‘iterate<bool>(const main()::<lambda(const int&)>&)’
48 | iterate< bool >( on_element );
| ^
main.cpp:10:6: note: candidate: ‘template<class Odds, class Evens, class Skip, class auto:11, class auto:12> void iterate(const auto:11&, const auto:12&)’
10 | void iterate(
| ^~~~~~~
main.cpp:10:6: note: template argument deduction/substitution failed:
main.cpp:48:33: note: candidate expects 2 arguments, 1 provided
48 | iterate< bool >( on_element );
| ^
main.cpp:51:39: error: no matching function for call to ‘iterate<void, bool>(const main()::<lambda(const int&)>&)’
51 | iterate< void, bool >( on_element );
| ^
main.cpp:10:6: note: candidate: ‘template<class Odds, class Evens, class Skip, class auto:11, class auto:12> void iterate(const auto:11&, const auto:12&)’
10 | void iterate(
| ^~~~~~~
main.cpp:10:6: note: template argument deduction/substitution failed:
main.cpp:51:39: note: candidate expects 2 arguments, 1 provided
51 | iterate< void, bool >( on_element );
| ^
有没有办法用 C++ 实现它? 也许有更好的编译时技术来实现所需的行为?
如果你需要iterate
的一个版本来接受一个参数,你必须实现一个。
在我看来,在这种情况下,您应该避免 auto
类型参数。
我提出以下代码
#include <type_traits>
#include <iostream>
#include <set>
using namespace std;
template<
bool Odds = true,
bool Evens = true,
typename Func,
typename Skip = bool
>
void iterate(
Func const & on_element,
Skip const & skip = false
) {
for ( int i = 0; i < 6; ++ i ) {
if constexpr ( ! is_same_v< Skip, bool > ) {
if ( skip.find( i ) != skip.end() )
continue;
}
if ( i % 2 == 1 ) {
if constexpr ( Odds )
on_element( i );
}
else {
if constexpr ( Evens )
on_element( i );
}
static_assert(
Odds || Evens,
"Either odds or evens are required to get traversed"
);
}
}
int main() {
const auto & on_element = []( const int & i ) {
cout << " " << i << endl;
};
cout << "Whole range:" << endl;
iterate( on_element );
cout << "Should skip specified elements:" << endl;
iterate( on_element, std::set{ 1, 2, 3 } );
cout << "Should traverse only evens:" << endl;
iterate< false >( on_element );
cout << "Should traverse only odds:" << endl;
iterate< true, false >( on_element );
// should NOT compile if uncomment:
// iterate< false, false >( on_element );
}
template<
bool Odds = false,
bool Evens = false,
bool Skip = false // or really, true: why pass skip if we won't use it?
>
void iterate(
const auto & on_element,
const auto & skip
)
并编写另一个重载:
template<
bool Odds = false,
bool Evens = false
> // no bool Skip; lack of arg is enough
void iterate(
const auto & on_element
)
{
iterate<Odds, Evens, false>(on_element);
}
然后将 ! is_same_v< Skip, void >
替换为 Skip
并类似替换 odds/evens。
还有
static_assert( !Skip||!std::is_same_v<decltype(skip), const int&>, "You must pass a 2nd argument" );
对生活质量有好处。
或许可以简化
#include <type_traits>
#include <iostream>
#include <algorithm>
#include <array>
auto Odds = [](auto&& i) { return i % 2 == 0; };
auto Evens = [](auto&& i) { return i % 2 == 1; };
auto None = [](auto&&) { return false; };
template<class...Ts>
struct These
{
constexpr These(Ts... args)
: args_ { args... }
{}
template<class Arg>
bool operator()(Arg&& i) const
{
return std::any_of(
std::begin(args_),
std::end(args_),
[i](auto x) { return x == i; }
);
}
std::array<std::common_type_t<Ts...>, sizeof...(Ts)> args_;
};
template<
typename Skip = decltype(None)
>
void iterate(
const auto & on_element,
Skip&& skip = Skip()
) {
for ( int i = 0; i < 6; ++ i ) {
if (skip(i))
continue;
std::cout << i << ", ";
}
std::cout << std::endl;
}
int main() {
const auto & on_element = []( const int & i ) {
std::cout << " " << i << std::endl;
};
std::cout << "Whole range:" << std::endl;
iterate( on_element );
std::cout << "Should skip specified elements:" << std::endl;
iterate( on_element, These( 1, 2, 3 ));
std::cout << "Should traverse only evens:" << std::endl;
iterate( on_element , Evens);
std::cout << "Should traverse only odds:" << std::endl;
iterate( on_element, Odds );
}
预期输出:
Whole range:
0, 1, 2, 3, 4, 5,
Should skip specified elements:
0, 4, 5,
Should traverse only evens:
0, 2, 4,
Should traverse only odds:
1, 3, 5,