Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is returning declval UB if the template is never called outside of unevaluated context?

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{};
}
like image 795
Alexandre S. Avatar asked Oct 14 '22 20:10

Alexandre S.


1 Answers

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.

like image 121
Davis Herring Avatar answered Oct 18 '22 22:10

Davis Herring