【问题标题】:Overloading linearly: why clang fails where gcc compiles?线性重载:为什么clang在gcc编译的地方失败?
【发布时间】:2017-03-12 05:04:30
【问题描述】:

根据here 的问题,我设计了这些实用程序来运行与给定参数列表兼容的第一个函数。但是,它使用 g++ 编译(在 -std=c++14 选项下),但由于 clang 而失败。是 clang 的 bug,还是 g++ 的 bug,以及如何让它在两个编译器上都工作。

// 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.

【问题讨论】:

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


    【解决方案1】:

    最简单的解决方法是完全摆脱有问题的constexpr 变量,幸运的是这很简单——因为_findex 已经返回了std::integral_constant,它在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,最好是两者的混合体。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多