3

First, I am really sorry for the poor quality of this code, but I have already spent 1 hour to isolate the source of my problems and I do not have a shorter example than this. So here is the code:

#include <iostream>
#include <type_traits>
#include <utility>
#include <tuple>
#include <array>

template <class Crtp, class... Types>
struct Base
{
    template <
        unsigned int Index,
        class Type = typename std::tuple_element<Index, std::tuple<Types...> >::type
    >
    inline const Type& get() const {
        return std::get<Index>(data);
    }

    template <
        unsigned int Index,
        class Type = typename std::tuple_element<Index, std::tuple<Types...> >::type
    >
    inline Crtp& set(const Type& value) {
        std::get<Index>(data) = value; return static_cast<Crtp&>(*this);
    }

    std::tuple<Types...> data;
};

template <typename Type, unsigned int Size>
struct Derived : public Base<Derived<Type, Size>, std::array<Type, Size>>
{
    template <
        class... Args,
        class Template = decltype(std::declval<const Base<
            Derived<Type, Size>,
            std::array<Type, Size>
        >>().template get<0>(std::declval<Args>()...))
    >
    inline Template test(Args&&... args) const {
         return this->template get<0>(std::forward<Args>(args)...);
    } 

    template <
        class... Args,
        class Template = decltype(std::declval<const Base<
           Derived<Type, Size>, 
           std::array<Type, Size>
        >>().template set<0>(std::declval<Args>()...))
    >
    inline Derived<Type, Size>& test(Args&&... args) {
        return this->template set<0>(std::forward<Args>(args)...);
    } 

    static void check() {
         Derived<double, 3> derived;
         std::cout<<derived.test()[0]<<std::endl;
    }
};

int main(int argc, char* argv[])
{
    Derived<double, 3> derived;
    std::cout<<derived.test()[0]<<std::endl; // Working

    Derived<double, 3>::check(); // Not working: error: no match for ‘operator[]’ (operand types are ‘Derived<double, 3u>’ and ‘int’)
    return 0;
}

Explanation of what is done: There is a Base class that takes a derived class (CRTP) and tuple types as template arguments. This base class has two members: one to get the n-th element of the tuple, the other to set the n-th element of the tuple. Then, there is a Derived class that inherits from the Base class and put a std::array in the tuple of the base class: consequently, the type of data of this derived class is : std::tuple<std::array<Type, Size>> data. This derived class has an overloaded function test() which calls the get or set function depending on its argument: test() will call get() but test(std::array<double, 3>{1, 2, 3}) will call set(std::array<double, 3>{1, 2, 3}). Consequently test()[0] should return the first element of the array: it works in the main(), but it does not work in a static function.

I do not know what the compiler is trying to do, but apparently this is not working. I think that this is a bug in g++ 4.8.1 (I have not tried other versions) but I wanted to be sure of that.

So here are my questions:

  • Can you confirm this bug (and maybe find an explanation)?
  • Do you have a shorter and less complicated example to illustrate the problem?
4

1 回答 1

1

“编译器正在尝试做的事情”是在 const 和非 const 重载之间错误地执行重载解析,Derived::test当在内部没有参数的情况下调用时check()。你可以通过插入看到

std::cout << typeid(decltype(derived.test())).name() << std::endl;

check()("7DerivedIdLj3EE") 和main()("St5arrayIdLy3EE") 中。

编辑:一些调查,交替注释掉 const/nonconst 测试重载,表明模板推导并没有因为 in 的非常量重载而失败,test因为check()它应该。参数包为空,因此在确定返回类型Args的表达式中应该发生替换失败,不带参数。decltypeset<0>

当您思考原因时,我建议您简化代码以避免它:

#include <iostream>
#include <utility>
#include <tuple>
#include <array>

template <class Crtp, class... Types>
struct Base
{
    std::tuple<Types...> data;

    template <unsigned int Index>
    auto get() const -> decltype(std::get<Index>(data)) {
        return std::get<Index>(data);
    }

    template <unsigned int Index, typename T>
    Crtp& set(T&& value) {
        std::get<Index>(data) = std::forward<T>(value);
        return static_cast<Crtp&>(*this);
    }
};

template <typename Type, unsigned int Size>
struct Derived : public Base<Derived<Type, Size>, std::array<Type, Size>>
{
    auto test() const -> decltype(this->template get<0>()) {
        return this->template get<0>();
    }

    template <typename T>
    Derived& test(T&& value) {
        return this->template set<0>(std::forward<T>(value));
    }

    static void check() {
        Derived<double, 3> derived;
        std::cout << derived.test()[0] << std::endl;
    }
};

int main(int, char*[])
{
    Derived<double, 3> derived;
    std::cout << derived.test()[0] << std::endl;
    Derived<double, 3>::check();
}
于 2013-07-16T06:22:00.917 回答