What if redefine an inline function?
Asked Answered
C

3

9

I've spent days in a weird problem and finally discover that there were two inline function of the same signature in the project and they caused the problem. To simplify the situation here is an example: two cpp file:

a.cpp

#include <iostream>

void b();

inline void echo()
{
    std::cout << 0 << std::endl;
}

int main()
{
    echo();
    b();
    return 0;
}

and b.cpp

#include <iostream>

inline void echo()
{
    std::cout << 1 << std::endl;
}

void b()
{
    echo();
}

Please note that inline functions echo have the same signature but different implements. Compile and run

g++ a.cpp b.cpp -o a.out && ./a.out

Or like this

g++ a.cpp -c
g++ b.cpp -c
g++ a.o b.o -o a.out
./a.out

It prints 0 0. (I was using g++ 4.6.1 for that, and I tested with clang++ 2.9, same result)

That won't happen if turning on optimization, like

g++ -O3 a.cpp b.cpp -o a.out && ./a.out

It is 0 1 this time.

My question is, no matter the result or how the compilation performs, there is no error or even warning about I have defined inline functions multiple times. What on earth happens to the compiler and linker in this kind of situation?

EDIT:

Take a look at the symbols in the object file

nm a.o b.o | c++filt

Both files have the record echo(). So I think the problem happens at the link time. Could it be said that the linker randomly picks one implementation and discard all other?

Coprolalia answered 27/7, 2011 at 10:58 Comment(2)
Have you tried higher warning verbosity (-Wall etc.)?Gasometer
I've just tried -Wall -Wextra, still no warning.Coprolalia
A
5

The compiler is not required to diagnose this ODR violation, and it is not trivial. The inline keyword means that different translation units might have the same symbol, so it is marked weak by the compiler. The basic use case is a function defined inline in a header: all translation units that include the header will have the definition, and it is perfectly fine. The compiler only needs to discard all but one definition and use that definition everywhere.

Detecting whether the different definitions are exact matches is a complex problem. The linker would have to analyze the generated binary implementation and determine whether the two binary codes relate to the same source code or not. Most compilers do not have support to determine this.

As of your particular problem, I cannot possibly know the rationale that led to the two functions being marked inline, but a common error is using the inline keyword to represent optimize rather than don't complain of repetitions at link time. The inline keyword makes sense in headers, but not so much in cpp files. In cpp files, if you want to factor some piece of code into a helper function, that function should be either marked static or be defined within an unnamed namespace. If the function is static then the compiler knows that all of the usages of that function are within your translation unit, and it has greater knowledge to decide whether it wants to inline or not the function call (note that it can inline even if you don't tell it to, in the same way that it can decide not to inline even if you tell it).

Anna answered 27/7, 2011 at 11:29 Comment(0)
B
13

In the C++ standard it is stated than all the definitions of an inline function shall be the same, but no diagnostic is required. That is, your program is not a valid C++ program, but an implementation has the right not to detect that error.

See Clause 3.2.5. It's too long to post here.

Banana answered 27/7, 2011 at 11:0 Comment(0)
A
6

This very case (two inline functions with the same name and same signatures having different implementations) leads to undefined behavior. The compiler is not required to diagnose it, although it could try to.

Atlantes answered 27/7, 2011 at 11:1 Comment(0)
A
5

The compiler is not required to diagnose this ODR violation, and it is not trivial. The inline keyword means that different translation units might have the same symbol, so it is marked weak by the compiler. The basic use case is a function defined inline in a header: all translation units that include the header will have the definition, and it is perfectly fine. The compiler only needs to discard all but one definition and use that definition everywhere.

Detecting whether the different definitions are exact matches is a complex problem. The linker would have to analyze the generated binary implementation and determine whether the two binary codes relate to the same source code or not. Most compilers do not have support to determine this.

As of your particular problem, I cannot possibly know the rationale that led to the two functions being marked inline, but a common error is using the inline keyword to represent optimize rather than don't complain of repetitions at link time. The inline keyword makes sense in headers, but not so much in cpp files. In cpp files, if you want to factor some piece of code into a helper function, that function should be either marked static or be defined within an unnamed namespace. If the function is static then the compiler knows that all of the usages of that function are within your translation unit, and it has greater knowledge to decide whether it wants to inline or not the function call (note that it can inline even if you don't tell it to, in the same way that it can decide not to inline even if you tell it).

Anna answered 27/7, 2011 at 11:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.