質問

In the comments to this answer, Koushik raised a very valid point.

Take the following:

union U
{
    int x;
    const T y;
};

(I choose T such that there is no common initial sequence of layout compatibility here, meaning only one member may be active at any given time per [C++11: 9.5/1].)

Since only one member may be "active" at any one time (made active by writing to it), and y cannot be written to after initialisation, isn't this rather pointless? I mean, y can only be read from until the first time x is written to, and at that only if y was the initialised member.

Is there some use case I'm missing? Or is this indeed a pretty pointless confluence of language features?

(This has been mentioned before)

役に立ちましたか?

解決

Here's a contrived example of a reference-semantics type where you'd only want to grant const access to. The union is used in a variant-like data type returned from a "type-erasing" function.

#include <memory>

template<class T>
struct reference_semantics
{
public:
    reference_semantics(T* p ) : m(p) {}

    int observe() const { return *m; }
    void change(T p) { *m = p; }

private:
    T* m;
};

struct variant
{
    enum T { INT, DOUBLE } type;

    union U
    {
        reference_semantics<int> const i;
        reference_semantics<double> const d;

        U(int* p) : i(p) {}
        U(double* p) : d(p) {}
    } u;
};

#include <iostream>
std::ostream& operator<<(std::ostream& o, variant const& v)
{
    switch(v.type)
    {
        case variant::INT:
            return o << "INT: "<<v.u.i.observe();
        case variant::DOUBLE:
            return o << "DOUBLE: "<<v.u.d.observe();
    }
}

#include <string>

variant type_erased_access(std::string name)
{
    // imagine accesses to a map or so

    static double dval = 42.21;
    static int ival = 1729;

    if(name == "Lightness") return { variant::DOUBLE, &dval };
    else return { variant::INT, &ival };
}

int main()
{
    variant v0( type_erased_access("Lightness") );
    std::cout << v0 << "\n";
    variant v1( type_erased_access("Darkness") );
    std::cout << v1 << "\n";
}

Imagine now that instead of int and double, much larger data types are used, and that the reference_semantics data type actually provides more functionality than just returning the value.

It might even be possible that you want to return a reference_semantics<some_type> const for some arguments, but a plain int for others. In that case, your union might even have const and non-const members.

他のヒント

It does have uses:

1) For offering a const_cast-like technique. In a sense, x = const_cast<...>(y).

2) When dealing with templates, sometimes you need a const version of a data type so you match other parameter types.

(I've seen (1) used when programming against legacy interfaces).

Not using unions a lot, but this might be scenario:

#include <iostream>

class Accessor;
union Union
{
    private:
    friend class Accessor;
    int write;

    public:
    const int read;

    Union() : read(0) {}
};

class Accessor  {
    public:
    static void apply(Union& u, int i) { u.write = i; }
};

int main() {
    Union u;
    // error: ‘int Union::write’ is private
    // u.write = 1;
    std::cout << u.read << '\n';
    Accessor::apply(u, 1);
    std::cout << u.read << '\n';
}

Note: From 9.5 Unions

Note: One special guarantee is made in order to simplify the use of unions: If a standard-layout union contains several standard-layout structs that share a common initial sequence (9.2), and if an object of this standard-layout union type contains one of the standard-layout structs, it is permitted to inspect the common initial sequence of any of standard-layout struct members; see 9.2. — end note ]

If the union represents part of a result of some method/algorithm, then it could make sense. But in that case, I'd make both values const:

union T
{
    const int x;
    const int y;
};
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top