I'm trying to create a simple triplet class, but the more I look at the implementation of std::pair
the more I feel like I'm missing important details. What I have feels "too simple". I am aware of std::tuple
and other options, but the use case is to be able to use uniform initialization -- so we can make certain assumptions / ignore complications (?)
I am restricted to c++11
, none of those beautiful c++14/17
magical unicorns added.
/// keeping class and test in self-contained file {test.cpp}
/// Compile: g++ -o test -std=c++11 test.cpp
#include <iostream>
#include <vector>
/// class definition
template <class T1, class T2, class T3>
struct triple {
/// q1
typedef T1 first_type;
typedef T2 second_type;
typedef T3 third_type;
T1 first;
T2 second;
T3 third;
/// q2
triple() : first(), second(), third() {}
triple(const T1 &f, const T2 &s, const T3 &t)
: first(f), second(s), third(t) {}
/// q3
template<class U1, class U2, class U3>
triple(const triple<U1, U2, U3> &t)
: first(t.first), second(t.second), third(t.third) {}
triple(const triple<T1, T2, T3> &t) = default;
triple(triple<T1, T2, T3> &&t) = default;
};
/// simple function to "use" these
void print(const std::vector<triple<int, const std::string, const std::string>> &vals) {
for(auto &tup : vals) {
std::cout << "First: " << tup.first << std::endl
<< "Second: " << tup.second << std::endl
<< "Third: " << tup.third << std::endl;
std::cout << "------------------" << std::endl;
}
}
/// simple main, goal: uniform initialization
int main(int argc, char **argv) {
print({
{1, "hi", "billy"},
{2, "you", "cool"},
{3, "no", "not"}
});
}
q1: the purpose of typedef
I know it's kind of old, but it's easier to explain from this implementation -- I'm looking at the implementation for clang
. Anyway, they all do this
typedef _T1 first_type; ///< @c first_type is the first bound type
typedef _T2 second_type; ///< @c second_type is the second bound type
and subsequently never use them. What is the purpose, just to be able to do something like typename std::pair<int, float>::second_type f = 0.012f;
?
q2: default constructors
My ultimate purpose is to just enable uniform initialization of something instead of a (large) number of std::make_tuple
calls. In this context, it seems reasonable to include some static_assert
for
is_trivially_constructible
is_trivially_default_constructible
is_trivially_move_constructible
is_trivially_destructible
Does adding these make sense or is that over-limiting for no real reason?
q3: reflection construction?
Concerning the constructor with U1
, U2
, and U3
, the purpose of this is to allow for automatic conversions? Say automatically converting a triple<unsigned int, float, bool>
-> triple<size_t, double, int>
?
q4: any alarms going off?
Like I said, this is intended to be a simple and non-robust class. I've been playing around with it, no memory leaks I can find, etc. The operator overloads would be easy to put in, but other than that is there anything I should be more concerned with? The stl implementation of pair
is much more complex, and being that I ditched std::tuple
so that I can do uniform initialization, this makes me think I'm missing something.