I needed a special kind of variadic container and had some unforseen problems, so I created the following minimal example (see comments):
#include <iostream>
#include <tuple>
//---------------------------------------------------
template<class...Ts>
class container
{
using ts_type = std::tuple<Ts...>;
public:
container() = default;
container(Ts... ts) : ts_{std::move(ts)...} {}
// get #1: member template
template<std::size_t index>
auto
get() const -> decltype(std::get<index>(std::declval<const ts_type>())) {
return std::get<index>(ts_);
}
// get #2: friend function
template<std::size_t index>
friend typename std::tuple_element<index,ts_type>::type
get(const container& c) {
return std::get<index>(c.ts_);
}
// get #3: friend function with type deduction
// won't compile
// error: 'const class container<int, double>' has no member named 'ts_'
//
// template<std::size_t index>
// friend auto
// get(const container& c) -> decltype(std::get<index>(c.ts_)) {
// return std::get<index>(c.ts_);
// }
private:
ts_type ts_;
};
//---------------------------------------------------
// WTF? g++ already complains about the declaration
// I'm not even trying to instantiate foo
template<class T>
void foo(const T& t) {
// error: expected primary-expression before ')' token
std::cout << t.get<0>() << std::endl;
// error: 'get' was not declared in this scope
std::cout << get<0>(t) << std::endl;
}
//---------------------------------------------------
int main() {
// this compiles and runs just fine ... as expected
auto c = container<int,double>{1, 2.5};
std::cout << c.get<0>() << std::endl;
std::cout << c.get<1>() << std::endl;
std::cout << get<0>(c) << std::endl;
std::cout << get<1>(c) << std::endl;
}
What is going on here? Why does g++ (4.7.2) complain about the declarations inside foo? Why does container::get #3 not compile?
I guess things like get<0> can only be invoked on concrete types?
Is this behavior standard conforming?