Safe to pass empty variables by value, when they have no definition?
Asked Answered
P

1

8

If I pass an empty variable by value, even though it has no definition, is it safe and compliant?

I came across this issue while working on code, overloading |, to make this print the contents of the vector v:

v | print; // prints the vector v

The code I present here works for me with g++ and clang, even though print is an extern variable with no linkage, but I wonder if I'm pushing the standard too far. This is for c++11/c++14, I guess this is solved in c++17 with inline variables?

First, my initial code. The goal is to allow things like v|print to print a vector. I have bigger goals too, related to ranges, but I'll focus on this tiny example here

struct print_tag_t {};

print_tag_t print;

void operator| (std::vector<int> & x, decltype(print) ) {
    for(auto elem : x) {
        std::cout << elem << '\n';
    }
}

int main() {
    std::vector<int>  v{2,3,5,7};
    v | print;
}

Moving this to a header

If I move this into a header, I can make the operator| overload as inline. But what about print? I have found that I can make it extern to avoid the linker error about duplicate symbols

// print.hh
struct print_tag_t {};

extern  // extern, as I can't use inline on a variable
print_tag_t print;

inline
void operator| (std::vector<int> & x, decltype(print) ) {
    for(auto elem : x) {
        std::cout << elem << '\n';
    }
}

This works for me. Somehow, even though print has no definition, I can do v|print. I guess it's because it's empty and therefore there is no value to inspect and therefore it never needs an address.

Are compilers required to allow my v|print example to work? Where, to clarify, print is extern and hasn't been given any definition?

Pluton answered 2/11, 2017 at 11:7 Comment(6)
Interesting idea. How about const struct print_tag_t {} print; in your header file? No problem with multiple definitions. `Cloe
But the linker should be complaining about the non-existence of "print_tag_t print;", or is that still in the cpp file? It would need to be somewhere, though you could do as suggested above, or use an anonymous namespace. Personally, when I need a token like this I declare an enum with 1 value, and use the value asa unique typed instance.Angkor
@GemTaylor, neither compiler complains about that. And I don't have that in any cpp file. But yes, it suggests g++ and clang are being a little lenient with mePluton
I suspect you are just being lucky with high optimisation settings. Though it is very difficult to make gcc be completely dumb, try -fno-inline ?Angkor
You can wrap print declaration in an anonymous namespace, so it is local to each translation unit, or declare it new-age inline or old-school static.Nathanson
The more usual thing to do here is to use an enum, e.g. enum actions { print }; and then overload for enum actions and you do not have to worry about the linkage issueRitualism
H
7

Are compilers required to allow my v|print example to work? Where, to clarify, print is extern and hasn't been given any definition?

No. You are odr-using print (you're invoking an lvalue-to-rvalue conversion that doesn't yield a constant expression), which means you need a definition for print. However, this is one of the categories of error that are ill-formed, no diagnostic required. And since the code in question doesn't take the address of print anywhere, it's likely that the compiler will emit code that will not require such a definition, and hence the linker will be happy about it too. Typically, it will Just Work™.

A better solution would be to simply change the declaration to make print constexpr:

constexpr print_tag_t print{};

Now, v | print wouldn't odr-use print (since the lvalue-to-rvalue conversion would now be a constant expression), so no definition is even necessary, so the program is well-formed.

Heda answered 2/11, 2017 at 12:27 Comment(4)
@geza The original question had mixed up linkage and definition, and the answer was mostly about linkage. With T.C.'s edit, the question makes more sense, and I edited the answer to reflect the better wording of the question.Heda
@ArneVogel No, that definitely odr-uses print. Now we're taking its address, so it needs a definition!Heda
D'oh! You're right. I was confusing this with complete vs. incomplete type.Junia
@AaronMcDaid That was T.C. I just added c++Heda

© 2022 - 2024 — McMap. All rights reserved.