Triple-type template container
Don't make it possible for your container to contain nothing. It can be wrapped in a std::optional for that. Drop the _init_ type and parameterless constructor.
Use val consistently. The parentheses around this-&gt;val are unnecessary anyway, but since you're already using val in some places, just use it everywhere.
Don't allow construction or assignment with values of bad types. That fails silently right now, which is not a good way to fail. Luckily, you can do even better and keep the static typing with overloading:

TripleType(A x) { val._e = _tA; val.__a = x; }
TripleType(B x) { val._e = _tB; val.__b = x; }
TripleType(C x) { val._e = _tC; val.__c = x; }

TripleType&amp; operator=(A x) { val._e = _tA; val.__a = x; return *this; }
TripleType&amp; operator=(B x) { val._e = _tB; val.__b = x; return *this; }
TripleType&amp; operator=(C x) { val._e = _tC; val.__c = x; return *this; }

Mark methods that don't modify their object &ndash; get() and current_type() &ndash; as const.
Don't allow fetching values of the wrong type. I'm not a C++ expert by any means, so there might be a better way of restricting T to A, B, or C while keeping the same .get&lt;T&gt; calling convention (I sure hope so), but:

private:
    A get_(A const*) const {
        if (val._e != _tA) {
            throw bad_multitype_access(typeid(A), current_type());
        }

        return val.__a;
    }

    B get_(B const*) const {
        if (val._e != _tB) {
            throw bad_multitype_access(typeid(B), current_type());
        }

        return val.__b;
    }

    C get_(C const*) const {
        if (val._e != _tC) {
            throw bad_multitype_access(typeid(C), current_type());
        }

        return val.__c;
    }

public:
    template &lt;class T&gt; T get() const {
        return get_(static_cast&lt;T const*&gt;(nullptr));
    }


where bad_multitype_access would be something along the lines of:

class bad_multitype_access : std::logic_error {
public:
    bad_multitype_access(std::type_info const&amp; expected, std::type_info const&amp; actual) :
        std::logic_error(std::string("Attempted to get ") + expected.name() + " from multitype containing " + actual.name()) {}
};


That's all verbose and a bit hacky, but prevents getting types outside of the expected set statically and values of the wrong type dynamically. Again, I would love to hear a better way of accomplishing the same thing from someone who actually knows the language. You can also drop the vp_t typedef at this point, which was never really a good idea, as well as is_same_type.
Use a switch for current_type().

std::type_info const&amp; current_type() const {
    switch (val._e) {
    case _tA: return typeid(A);
    case _tB: return typeid(B);
    case _tC: return typeid(C);
    }
}

Stop it with the underscores already! =)

private:
    enum contained_type {
        tA,
        tB,
        tC,
    };

    struct {
        A a;
        B b;
        C c;
        contained_type type;
    } val;

It feels like creating a TripleType shouldn't create an instance of each of its possible types, and that writing a new value of a different type should destroy a previously existing one. unique_ptrs could work here.

private:
    ⋮
    struct {
        std::unique_ptr&lt;A&gt; a;
        std::unique_ptr&lt;B&gt; b;
        std::unique_ptr&lt;C&gt; c;
        contained_type type;
    } val;
    ⋮
        return *val.a;
    ⋮
        return *val.b;
    ⋮
        return *val.c;

public:
    TripleType(A x) { val.type = tA; val.a = std::make_unique&lt;A&gt;(x); }
    TripleType(B x) { val.type = tB; val.b = std::make_unique&lt;B&gt;(x); }
    TripleType(C x) { val.type = tC; val.c = std::make_unique&lt;C&gt;(x); }

    TripleType&amp; operator=(A x) {
        val.type = tA;
        val.a = std::make_unique&lt;A&gt;(x);
        val.b.reset(nullptr);
        val.c.reset(nullptr);
        return *this;
    }

    TripleType&amp; operator=(B x) {
        val.type = tB;
        val.a.reset(nullptr);
        val.b = std::make_unique&lt;B&gt;(x);
        val.c.reset(nullptr);
        return *this;
    }

    TripleType&amp; operator=(C x) {
        val.type = tC;
        val.a.reset(nullptr);
        val.b.reset(nullptr);
        val.c = std::make_unique&lt;C&gt;(x);
        return *this;
    }

Avoid the double-copy-construct by accepting T const&amp; instead of T in the constructors and operator=s.
Since TripleType is managing everything about val, move val's members into it; it's just doing namespacing right now and not representing an actual object.
Give TripleType some copy and move constructors.

TripleType(TripleType&lt;A, B, C&gt; const&amp; o) {
    type = o.type;

    switch (type) {
    case tA: a = std::make_unique&lt;A&gt;(*o.a); break;
    case tB: b = std::make_unique&lt;B&gt;(*o.b); break;
    case tC: c = std::make_unique&lt;C&gt;(*o.c); break;
    }
}

TripleType(TripleType&lt;A, B, C&gt;&amp;&amp; o) = default;



All told, it looks like this now:

#ifndef MULTITYPE_HPP
#define MULTITYPE_HPP

#include &lt;memory&gt;
#include &lt;stdexcept&gt;

class bad_multitype_access : std::logic_error {
public:
    bad_multitype_access(std::type_info const&amp; expected, std::type_info const&amp; actual) :
        std::logic_error(std::string("Attempted to get ") + expected.name() + " from multitype containing " + actual.name()) {}
};

template &lt;class A, class B, class C&gt;
class TripleType {
private:
    enum contained_type {
        tA,
        tB,
        tC,
    };

    std::unique_ptr&lt;A&gt; a;
    std::unique_ptr&lt;B&gt; b;
    std::unique_ptr&lt;C&gt; c;
    contained_type type;

    A get_(A const*) const {
        if (type != tA) {
            throw bad_multitype_access(typeid(A), current_type());
        }

        return *a;
    }

    B get_(B const*) const {
        if (type != tB) {
            throw bad_multitype_access(typeid(B), current_type());
        }

        return *b;
    }

    C get_(C const*) const {
        if (type != tC) {
            throw bad_multitype_access(typeid(C), current_type());
        }

        return *c;
    }

public:
    TripleType(TripleType&lt;A, B, C&gt; const&amp; o) {
        type = o.type;

        switch (type) {
        case tA: a = std::make_unique&lt;A&gt;(*o.a); break;
        case tB: b = std::make_unique&lt;B&gt;(*o.b); break;
        case tC: c = std::make_unique&lt;C&gt;(*o.c); break;
        }
    }

    TripleType(TripleType&lt;A, B, C&gt;&amp;&amp; o) = default;

    TripleType(A const&amp; x) { type = tA; a = std::make_unique&lt;A&gt;(x); }
    TripleType(B const&amp; x) { type = tB; b = std::make_unique&lt;B&gt;(x); }
    TripleType(C const&amp; x) { type = tC; c = std::make_unique&lt;C&gt;(x); }

    TripleType&amp; operator=(A const&amp; x) {
        type = tA;
        a = std::make_unique&lt;A&gt;(x);
        b.reset(nullptr);
        c.reset(nullptr);
        return *this;
    }

    TripleType&amp; operator=(B const&amp; x) {
        type = tB;
        a.reset(nullptr);
        b = std::make_unique&lt;B&gt;(x);
        c.reset(nullptr);
        return *this;
    }

    TripleType&amp; operator=(C const&amp; x) {
        type = tC;
        a.reset(nullptr);
        b.reset(nullptr);
        c = std::make_unique&lt;C&gt;(x);
        return *this;
    }

    template &lt;class T&gt; T get() const {
        return get_(static_cast&lt;T const*&gt;(nullptr));
    }

    std::type_info const&amp; current_type() const noexcept {
        switch (type) {
        case tA: return typeid(A);
        case tB: return typeid(B);
        case tC: return typeid(C);
        }
    }
};
#endif


And now there's more type safety. (I hope this isn't actually egregiously bad advice!)

Also, here are some moving constructors and operator=s you can add.

TripleType(A&amp;&amp; x) { type = tA; a = std::make_unique&lt;A&gt;(std::move(x)); }
TripleType(B&amp;&amp; x) { type = tB; b = std::make_unique&lt;B&gt;(std::move(x)); }
TripleType(C&amp;&amp; x) { type = tC; c = std::make_unique&lt;C&gt;(std::move(x)); }

TripleType&amp; operator=(A&amp;&amp; x) {
    type = tA;
    a = std::make_unique&lt;A&gt;(std::move(x));
    b.reset(nullptr);
    c.reset(nullptr);
    return *this;
}

TripleType&amp; operator=(B&amp;&amp; x) {
    type = tB;
    a.reset(nullptr);
    b = std::make_unique&lt;B&gt;(std::move(x));
    c.reset(nullptr);
    return *this;
}

TripleType&amp; operator=(C&amp;&amp; x) {
    type = tC;
    a.reset(nullptr);
    b.reset(nullptr);
    c = std::make_unique&lt;C&gt;(std::move(x));
    return *this;
}