38

I have compiled and ran the following program in a C++17 compiler (Coliru). In the program, I declared an extern variable, but did not define it. However, the compiler doesn't give a linker error.

#include <iostream>

extern int i; // Only declaration

int func() 
{
    if constexpr (true)
        return 0;
    else if (i)
        return i;
    else
        return -1;
}

int main() 
{
    int ret = func();
    std::cout<<"Ret : "<<ret<<std::endl;
}

Why doesn't the compiler give a linker error?

msc
  • 33,420
  • 29
  • 119
  • 214

4 Answers4

58

Because the variable isn't odr-used. You have a constexpr if there that always discards the branch that could use it.

One of the points of constexpr if is that the discarded branch need not even compile, only be well-formed. That's how we can place calls to non-existing member functions in a discarded branch.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
41

In your case the variable is used in discarded statements only. However, even if we ignore that fact, C++ language specification still explicitly states that no diagnostic is required for missing definitions

3.2 One-definition rule

4 Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program outside of a discarded statement (6.4.1); no diagnostic required.

The language specification understands that an optimizing compiler might be smart enough to eliminate all odr-uses of a variable. In that case it would be excessive and unnecessary to require the implementation to detect and report the potential ODR violations.

Community
  • 1
  • 1
AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 3
    Even if there is no diagnostic required, doesn't it still mean the program is ill formed and thus shouldn't pass compilation & linking? – Hatted Rooster Jul 11 '17 at 07:36
  • 13
    @Rick Astley: "Ill-formed" never meant "shouldn't pass compilation & linking". Implementations are allowed to "successfully" compile absolutely anything, as long as they issued diagnostics where diagnostics is required by the language specification. In fact, the only feature that is required to actually *terminate* compilation in C++ is `#error` directive. – AnT stands with Russia Jul 11 '17 at 07:38
  • 2
    @AnT: Almost correct, except that the compiler does not even have to stop processing after `#error`, it is *only* required to issue a diagnostic, and the program is ill-formed (whereas `#warning` does not render the program ill-formed). See §16.5 [N4140, cpp.error]. (Update: `#warning` is not part of the standard.) – Arne Vogel Jul 11 '17 at 12:01
  • There's also `static_assert`, not that it matters. – Deduplicator Jul 11 '17 at 12:47
  • 1
    @Deduplicator `static_assert(!"do not format harddrive");` can produce an executable that formats the hard drive. It must also generate a diagnostic, but the standard places zero restriction on what a compiler does with an ill-formed program. Not that this matters, as only a hostile compiler is likely to do that. – Yakk - Adam Nevraumont Jul 11 '17 at 19:09
9

Because the compiler produces compiler errors, the linker would yield linker errors ...

No, seriously:

if constexpr (true)

is always true, so the compiler ignores the rest of the if-clause because it is never reached. So i is never used actually.

Rene
  • 2,466
  • 1
  • 12
  • 18
1

This has alrady been answered, but if you are interested, cppreference.com has exactly this example for constexpr if:

Constexpr If

The statement that begins with if constexpr is known as the constexpr if statement.

In a constexpr if statement, the value of condition must be a contextually converted constant expression of type bool. If the value is true, then statement-false is discarded (if present), otherwise, statement-true is discarded.
[...]
The discarded statement can odr-use a variable that is not defined:

extern int x; // no definition of x required
int f() {
if constexpr (true)
    return 0;
else if (x)
    return x;
else
    return -x;
}
Community
  • 1
  • 1