线性超载:为什么clang在gcc编译的地方失败了?

时间:2017-03-12 05:04:30

标签: c++ clang c++14 template-meta-programming sfinae

在问题here之后,我设计了这些实用程序,它们运行与给定参数列表兼容的第一个函数。但是,它使用g ++编译(在-std=c++14选项下),但是使用clang失败。这是一个铿锵的错误,它是一个g ++的错误,以及如何让它在两个编译器上工作。

// Include
#include <tuple>
#include <utility>
#include <iostream>
#include <type_traits>

// Temporary linear overload definition
template <class... F>
class temporary_linear_overload final
{
    // Types
    private:
    struct _default final
    {
        template <class... Args> 
        constexpr void operator()(Args&&...) noexcept
        {
        }
    };
    template <class... G>
    using _self = temporary_linear_overload<G...>;
    using _lvalue_reference = temporary_linear_overload&;
    using _rvalue_reference = temporary_linear_overload&&;
    using _const_lvalue_reference = const temporary_linear_overload&;
    using _const_rvalue_reference = const temporary_linear_overload&&;
    using _ftuple = std::tuple<F..., _default>;
    template <std::size_t N>
    using _ftype = typename std::tuple_element<N, _ftuple>::type;
    template <class T, std::size_t N>
    using _fconstant = std::integral_constant<std::size_t, N>;

    // Lifecycle
    private:
    temporary_linear_overload(_rvalue_reference x) = default;
    temporary_linear_overload(_const_lvalue_reference) = delete;
    temporary_linear_overload& operator=(_rvalue_reference) = delete;
    temporary_linear_overload& operator=(_const_lvalue_reference) = delete;
    template <class... G>
    explicit constexpr temporary_linear_overload(G&&... g) noexcept
    : _f{std::forward<G>(g)..., _default{}}
    {
    }

    // Function index
    template <std::size_t N = 0> 
    static constexpr auto _findex() -> _fconstant<
        decltype(std::declval<_ftype<N>>()()), N
    >
    {
        return _fconstant<void, N>();
    }
    template <std::size_t N = 0, class Arg, class... Args> 
    static constexpr auto _findex(Arg&& arg, Args&&... args) -> _fconstant<
        decltype(std::declval<_ftype<N>>()(
            std::forward<Arg>(arg), 
            std::forward<Args>(args)...)
        ),
        N
    >
    {
        return _fconstant<void, N>();
    }
    template <std::size_t N = 0, class... Args> 
    static constexpr auto _findex(Args&&... args)
    {
        return _findex<N + 1>(std::forward<Args>(args)...);
    }

    // Application
    public:
    template <class... Args> 
    decltype(auto) operator()(Args&&... args) &&
    {
        constexpr std::size_t index = _findex(std::forward<Args>(args)...);
        return std::get<index>(_f)(std::forward<Args>(args)...);
    }

    // Temporary creator
    public:
    template <class... G>
    friend constexpr _self<G...> overload_linearly(G&&... g) noexcept;

    // Data members
    private:
    _ftuple _f;
};

// Overload linearly definition
template <class... F>
constexpr temporary_linear_overload<F...> overload_linearly(F&&... f) noexcept
{
    return temporary_linear_overload<F...>(std::forward<F>(f)...);
}

// Example
int main(int argc, char* argv[])
{
    auto f0 = [](auto i){std::cout<<i<<std::endl;};
    auto f1 = [](auto i, auto j){std::cout<<i<<" "<<j<<std::endl;};
    overload_linearly(f0, f1)(1, 2.0);
    // runs f1 because it's the 1st valid function with the provided arguments
    return 0;
}

clang++ 3.8.0的错误消息:

overload_linearly_v2.cpp:75:66: error: constexpr variable 'index' must be initialized by a constant expression
        constexpr std::size_t index = _findex(std::forward<Args>(args)...);
                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~
overload_linearly_v2.cpp:101:30: note: in instantiation of function template specialization 'temporary_linear_overload<(lambda at overload_linearly_v2.cpp:99:15) &, (lambda at overload_linearly_v2.cpp:100:15)
      &>::operator()<int, double>' requested here
    overload_linearly(f0, f1)(1, 2.0);
                             ^
overload_linearly_v2.cpp:76:16: error: no matching function for call to 'get'
        return std::get<index>(_f)(std::forward<Args>(args)...);
               ^~~~~~~~~~~~~~~
/usr/lib/gcc/x86_64-linux-gnu/6.2.0/../../../../include/c++/6.2.0/utility:202:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '_Int'
    get(std::pair<_Tp1, _Tp2>& __in) noexcept
    ^
/usr/lib/gcc/x86_64-linux-gnu/6.2.0/../../../../include/c++/6.2.0/utility:207:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '_Int'
    get(std::pair<_Tp1, _Tp2>&& __in) noexcept
    ^
/usr/lib/gcc/x86_64-linux-gnu/6.2.0/../../../../include/c++/6.2.0/utility:212:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '_Int'
    get(const std::pair<_Tp1, _Tp2>& __in) noexcept
    ^
/usr/lib/gcc/x86_64-linux-gnu/6.2.0/../../../../include/c++/6.2.0/utility:221:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '_Tp'
    get(pair<_Tp, _Up>& __p) noexcept
    ^
/usr/lib/gcc/x86_64-linux-gnu/6.2.0/../../../../include/c++/6.2.0/utility:226:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '_Tp'
    get(const pair<_Tp, _Up>& __p) noexcept
    ^
/usr/lib/gcc/x86_64-linux-gnu/6.2.0/../../../../include/c++/6.2.0/utility:231:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '_Tp'
    get(pair<_Tp, _Up>&& __p) noexcept
    ^
/usr/lib/gcc/x86_64-linux-gnu/6.2.0/../../../../include/c++/6.2.0/utility:236:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '_Tp'
    get(pair<_Up, _Tp>& __p) noexcept
    ^
/usr/lib/gcc/x86_64-linux-gnu/6.2.0/../../../../include/c++/6.2.0/utility:241:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '_Tp'
    get(const pair<_Up, _Tp>& __p) noexcept
    ^
/usr/lib/gcc/x86_64-linux-gnu/6.2.0/../../../../include/c++/6.2.0/utility:246:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '_Tp'
    get(pair<_Up, _Tp>&& __p) noexcept
    ^
/usr/lib/gcc/x86_64-linux-gnu/6.2.0/../../../../include/c++/6.2.0/array:281:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '_Int'
    get(array<_Tp, _Nm>& __arr) noexcept
    ^
/usr/lib/gcc/x86_64-linux-gnu/6.2.0/../../../../include/c++/6.2.0/array:290:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '_Int'
    get(array<_Tp, _Nm>&& __arr) noexcept
    ^
/usr/lib/gcc/x86_64-linux-gnu/6.2.0/../../../../include/c++/6.2.0/array:298:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '_Int'
    get(const array<_Tp, _Nm>& __arr) noexcept
    ^
/usr/lib/gcc/x86_64-linux-gnu/6.2.0/../../../../include/c++/6.2.0/tuple:1254:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '__i'
    get(tuple<_Elements...>& __t) noexcept
    ^
/usr/lib/gcc/x86_64-linux-gnu/6.2.0/../../../../include/c++/6.2.0/tuple:1260:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '__i'
    get(const tuple<_Elements...>& __t) noexcept
    ^
/usr/lib/gcc/x86_64-linux-gnu/6.2.0/../../../../include/c++/6.2.0/tuple:1266:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '__i'
    get(tuple<_Elements...>&& __t) noexcept
    ^
/usr/lib/gcc/x86_64-linux-gnu/6.2.0/../../../../include/c++/6.2.0/tuple:1289:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '_Tp'
    get(tuple<_Types...>& __t) noexcept
    ^
/usr/lib/gcc/x86_64-linux-gnu/6.2.0/../../../../include/c++/6.2.0/tuple:1295:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '_Tp'
    get(tuple<_Types...>&& __t) noexcept
    ^
/usr/lib/gcc/x86_64-linux-gnu/6.2.0/../../../../include/c++/6.2.0/tuple:1301:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '_Tp'
    get(const tuple<_Types...>& __t) noexcept
    ^
2 errors generated.

1 个答案:

答案 0 :(得分:3)

最简单的解决方法是完全摆脱有问题的constexpr变量,幸运的是,这是微不足道的 - 因为_findex已经返回std::integral_constant,它会在<<}中编码您想要的值em> type ,您可以将operator()的定义替换为:

template <class... Args> 
decltype(auto) operator()(Args&&... args) &&
{
    using index = decltype(_findex(std::forward<Args>(args)...));
    return std::get<index::value>(_f)(std::forward<Args>(args)...);
}

Online Demo

或者,您可以重新实现_findex,使其仅传递参数 types ,而不是参数本身,这样可以保留当前有问题的变量:

// Function index
template <std::size_t N, class... Args>
static constexpr auto _findex(int) -> _fconstant<
    decltype(std::declval<_ftype<N>>()(std::declval<Args>()...)),
    N
>
{
    return {};
}

template <std::size_t N, class... Args>
static constexpr auto _findex(long)
{
    return _findex<N + 1, Args...>(0);
}

// Application
template <class... Args> 
decltype(auto) operator()(Args&&... args) &&
{
    constexpr std::size_t index = _findex<0, Args&&...>(0);
    return std::get<index>(_f)(std::forward<Args>(args)...);
}

Online Demo

IMO,两者的混合都是最好的。