Meta-Programming with a Method Parameter

2019-08-11 14:32发布

问题:

I'm writing a pair wrapper. For the purposes of this question it can be simplified down to:

using namespace std;

template <class T1, class T2>
class myPair {
    pair<T1, T2> member;
public:
    myPair() = default;
    myPair(T1 x, T2 y) : member(make_pair(x, y)) {}
};

I'd like to be able to treat a myPair as an index-able container of size 2. To do this I'd obviously need to write an index operator for myPair. I'd like to do something like this, but my return type will depend upon a method parameter, and I can't use method parameters in meta-programming.

auto myPair::operator[](int index) {
    static_assert(index >= 0 && index < 2, "Index out of range");

    return get<index>(*this);
}

Obviously I could tackle this in the same way that pair does by providing a get function, but I'd like to me able to use the index operator syntax. Is there any way I can specialize a function template or use a method parameter to meta-program a template's return type?

回答1:

It's almost possible. The integer literal can't be used directly as a constant-expression, but it can be wrapped in one, for example a template instantiation.

template <int> struct idx_ {};

template <char... C>
auto operator ""_i () {
    return idx_<(C - '0')...>{};
}

template <class T1, class T2>
class myPair {
    std::pair<T1, T2> member;
public:
    myPair() = default;
    myPair(T1 x, T2 y) : member(std::make_pair(x, y)) {}

    T1 &operator [] (idx_<0>) {
        return member.first;
    }

    T2 &operator [] (idx_<1>) {
        return member.second;
    }
};

int main() {
    myPair<int, std::string> mp(42, "Hi");
    std::cout << mp[0_i] << ", " << mp[1_i] << '\n';
}

Output:

42, Hi

Live on Coliru



回答2:

No, it's not possible. The member function operator[] accepts non-constexpr objects, therefore making a compile time type detection pretty much impossible.

This will also make the static_assert not compile, for the same reason.



回答3:

You may use std::intergral_constant

template <std::size_t N>
const auto& operator[](std::integral_constant<std::size_t, N>) const {
    static_assert(N < 2, "Index out of range");

    return std::get<N>(member);
}

template <std::size_t N>
auto& operator[](std::integral_constant<std::size_t, N>) {
    static_assert(N < 2, "Index out of range");

    return std::get<N>(member);
}

Live Demo