c++visual-c++staticwarnings

What is a good solution to avoid "unreferenced function with internal linkage has been removed" warnings?


I implement some functions in header files (template functions, for instance), but then some of these functions go unused in some translations units:

main.cpp

#include "include.h"

int main() {}

include.h

#pragma once

namespace my_namespace {

static void foo1() {}

static void inline foo2() {}

}

Unfortunately, this raises (at warning level /W4)

(5): warning C4505: 'my_namespace::foo1': unreferenced function with internal linkage has been removed

I have found that making a function static inline suppresses that warning - but only to make it reappear (at warning level /Wall) as

(7): warning C4514: 'my_namespace::foo2': unreferenced inline function has been removed

I have also found that anonymous namespaces are an option, but my functions already are in namespaces.

So, what should I do to suppress these compiler warnings?

Should I suppress them? Should I at least do static inline to move the warning from level 4 to level all?

I should add that the include is used in different translation units across the same library, so simply removing static is not an option.


Solution

  • Should I suppress [these compiler warnings]?

    No. You should never suppress compiler warnings until you know why they have been emitted. And even then, you should prefer to fix the underlying issue rather than suppressing the warning.

    Reason for the warning

    Why should one be concerned that an "unreferenced function with internal linkage has been removed"? Well, the removal is good, as it reduces your binary size (less bloat). The concern is really that an unreferenced function with internal linkage exists in the first place. This often indicates an oversight, either in not calling the function after defining it or in not removing the definition when the last call site was removed. However, you do have an unusual scenario where this usual cause of the warning does not apply.

    With the usual cause eliminated, should one go ahead and suppress the warning? Not yet. The usual cause assumes that the function in question was defined in a source file for use only within that source file. Not only does the usual cause of the warning not apply, but you are not even in the usual scenario. That suggests a misuse of something, in this case of internal linkage. You should fix that misuse instead of suppressing the warning.

    What's misused?

    Internal linkage is a tool to help avoid name conflicts. A function with internal linkage exists in a single translation unit and cannot be invoked from any other translation unit, not even by accident. Thus, the name of such a function does not need to be unique with respect to functions defined in other translation units. This gives you a simple way to define helper functions within a source file without affecting other translation units.

    Internal linkage implies that only one translation unit is involved. Headers imply (potential) use in multiple translation units. The two concepts are at odds with each other.

    Mixing headers and internal linkage is typically wrong, as it leads to waste, maybe worse. Typically. To the point where if you think you have an exceptional case, you do not. If you have an exceptional case, you know it is exceptional, probably because you want what would normally be "waste". In the vast majority of cases, header files should not define static non-member functions, nor should they contain anonymous namespaces.

    What to do?

    A function defined in a header file is potentially defined in multiple translation units. This runs afoul of the one-definition rule unless the function has internal linkage or is declared inline. The latter is what you want, and just the latter (inline, not static inline).

    Why?

    There is a significant difference between the two approaches for avoiding a violation of the one-definition rule. The violation would occur if there are more function definitions than function entities. Using internal linkage (e.g. static) gets around this by increasing the number of entities to match the number of definitions. Each entity is a copy of your function, so this approach increases the size of your compiled code.

    In contrast, the inline keyword resolves the violation by decreasing the number of definitions to match the number of entities. This keyword is a promise that all definitions for this entity are identical, hence they can be treated as a single definition. There are no needless copies unless you combine this with static. (If you use both static and inline, the number of entities increases before multiple definitions are consolidated.)

    #pragma once
    
    namespace my_namespace {
    
    inline void foo1() {}
    
    inline void foo2() {}
    
    }