I am writing some metafunctions, and I had the idea to write them using C++17's if constexpr combined with deduced return type. Here is an example :
#include <tuple>
#include <type_traits>
#include <string>
template<typename TupleType, std::size_t I = 0, typename... ReturnData>
constexpr auto filterImpl()
{
if constexpr (I < std::tuple_size_v<TupleType>)
{
using Type = std::tuple_element_t<I, TupleType>;
if constexpr (std::is_arithmetic_v<Type>)
return filterImpl<TupleType, I + 1, ReturnData..., Type>();
else
return filterImpl<TupleType, I + 1, ReturnData...>();
}
else
return std::declval<std::tuple<ReturnData...>>();
}
template<typename Tuple>
using FilterTuple = decltype(filterImpl<Tuple>());
//std::tuple<int, float>
using MyTuple = FilterTuple<std::tuple<int, std::string, float, int*>>;
int main(int argc, char* argv[])
{
MyTuple t{ 3, 5.f };
return 0;
}
Of course, you could probably use a simpler pattern for this problem, but this just a basic example of what I want to do.
This code compiles on msvc, but doesn't on onlinegdb. Which one is right? Is using declval this way authorized? Should I even write my metafunctions like that, or should I go back to template classes?
(I think that, if there is a way to do so, the more complex metafunctions could end up easier to read this way)
A mcve:
#include <utility>
template<bool b>
constexpr auto evil()
{
if constexpr (b)
return 0;
else
return std::declval<int>();
}
template<bool b>
using Test = decltype(evil<b>());
int main()
{
Test<false> t{};
}
Because you must instantiate the function template to determine its return type, there is a function that odr-uses std::declval<…>
, so the program is ill-formed. MSVC is in error to accept it (without a warning), although it could be argued that this rule oughtn’t require a diagnostic to allow the obvious implementation strategy of just failing to provide a definition forstd::declval
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With