Fact-checked by Grok 2 weeks ago

One Definition Rule

The One Definition Rule (ODR) is a core language rule in the C++ programming that prohibits more than one definition of any , , type, enumeration type, template, or other definable item within a single translation unit, while requiring exactly one such definition for every non-inline, odr-used entity across the entire program to ensure consistent linking and behavior. This rule, detailed in section [basic.def.odr] of the ISO/IEC 14882 , applies to entities that are "odr-used," meaning they are referenced in a way that requires their address, value, or definition to be known at link time, such as in potentially-evaluated expressions or non-static data member initializers. Violations of the ODR, such as providing differing definitions in multiple translation units, result in , and compilers are not required to issue diagnostics unless the entity is part of a named module with reachable prior definitions. Exceptions to the strict single-definition requirement allow multiple identical definitions for specific cases, including inline functions and variables (which must be defined in every translation unit where odr-used), and types, and templated entities, provided all definitions appear in different translation units, consist of the same token sequence (ignoring certain whitespace and comments), resolve to the same entities via name lookup, and share the same language linkage. For non-inline functions or variables with external linkage, any multiple definitions render the program ill-formed, potentially leading to linker errors or subtle runtime issues if undetected. The ODR facilitates modular C++ development by enabling header files to declare but not fully define non-inline entities, while promoting the use of inline and mechanisms to avoid duplication; adherence is enforced through practices like avoiding definitions in headers for non-inline items and using explicit for . Since its formalization in early C++ standards, the ODR has evolved to accommodate in C++20 and later, where definition domains (e.g., module fragments) further refine and odr-usability constraints.

Overview

Definition and Scope

The One Definition Rule (ODR) is a fundamental constraint in , as specified in the standard clause [basic.def.odr]. It requires that no translation unit shall contain more than one of any definable item, including variables, functions, types, enumeration types, concepts (since ), and templates. Multiple declarations of these entities are permitted within a single translation unit or across translation units, but definitions—those that fully specify the entity's or initial value—must adhere strictly to this uniqueness. For non-inline functions and variables that are odr-used (i.e., their names appear in a potentially-evaluated context that requires the definition), exactly one must exist in the entire program, with resulting from violations, though compilers are not required to diagnose them. The scope of the ODR encompasses a broad set of program entities to ensure consistent linkage and behavior across the compilation model. It applies to non-inline functions and variables, which must have a single program-wide; and types, which permit one definition per unit but require identical definitions across units; and templates, including their specializations, which follow similar per-unit uniqueness with linkage consistency. Concepts, introduced in C++20, are also subject to the ODR, treating them as templated entities that demand unique definitions within units. The explicitly distinguishes from : a introduces a name but does not necessarily provide a complete description (e.g., a or forward ), whereas a does (e.g., a function body or body with members). A translation serves as the foundational scope for ODR enforcement and is defined as the output of the preprocessing phase applied to a single source file, including all included header files and macro expansions, resulting in a sequence of tokens ready for . This boundary is critical because the ODR allows certain entities (like types and inline functions) to be defined in every translation where needed, provided the definitions are identical in token sequence and semantics across the program. The ODR has evolved across C++ standards to accommodate language extensions while preserving linkage integrity. It was first formalized in C++98 for core entities like variables, functions, classes, enumerations, and templates. refined it to support constexpr functions and variables, allowing their use in constant expressions without violating uniqueness when odr-used at . introduced inline variables, permitting their definition in every translation unit where odr-used, similar to inline functions, to facilitate libraries. In C++20, the addition of and modules further adjusted ODR application: follow template-like rules, while modules enable explicit import of definitions, reducing implicit inclusion risks and enhancing ODR compliance through named modules and export controls.

Purpose and Importance

The One Definition Rule (ODR) serves as a cornerstone of C++'s separate compilation model, allowing developers to declare entities like functions, variables, classes, and templates in header files for reuse across multiple translation units while requiring exactly one definition per entity in the entire program. This design enables by separating declarations from , avoiding code duplication and promoting maintainability in large projects. Without the ODR, including definitions in headers would lead to multiple identical copies during linking, potentially causing conflicts or inefficiencies. The rule's importance lies in its role during the linking phase, where it ensures the linker resolves a single, consistent for each odr-used , preventing errors from mismatched or duplicate implementations. In multi-file projects and libraries, ODR violations can produce , such as subtle runtime discrepancies if compilers optimize based on differing views of the same across translation units. By bridging independent compilation of translation units with whole-program assembly, the ODR supports scalable builds and reliable integration of components. Historically, the ODR evolved to address C's more permissive approach, where compatible type definitions could vary across files; C++'s enhanced and features like classes demanded program-wide consistency to avoid inconsistencies in object layouts or semantics. This stricter enforcement facilitated C++'s growth from C, enabling optimizations and features that assume a unified entity view. In modern C++, the ODR is particularly vital for modules, which impose stricter compliance by tying definitions to module purviews and interfaces, thereby minimizing violation risks inherent in traditional header inclusions and improving encapsulation and compilation speed.

Core Principles

The Basic Rule

The One Definition Rule (ODR) in C++ prohibits multiple definitions of certain entities within the same translation unit and imposes strict uniqueness requirements across the entire . Specifically, no translation unit shall contain more than one definition of any non-inline , non-inline , type, or enumeration type. For non-inline functions and that are odr-used in the , there must be exactly one across all translation units; additional definitions in other units render the ill-formed. This ensures that the linker can unambiguously resolve references without conflicts, maintaining consistent behavior throughout the executable. Class types and enumeration types follow a per-translation-unit for , but any such appearing in multiple translation units must be identical in a precise manner to satisfy the ODR. Equivalence requires that the have the same sequence of tokens, that name lookup and overload resolution in each yield the same entities, and that attributes like language linkage and constant initialization match exactly. For instance, reordering member functions in a or altering the underlying type of an across units would violate this semantic equivalence, even if the overall structure appears similar. Pure declarations, such as extern int x;, do not constitute and thus fall outside the ODR's prohibitions. Prior to , the ODR imposed stricter requirements on static constant integral data members of es, mandating an out-of-class if the member was odr-used, despite allowing in-class initialization. C++17 and later standards relaxed this by making constexpr static data members implicitly inline, permitting their solely within the class without additional out-of-class declarations even if odr-used. These changes in [basic.def.odr] paragraphs 3 through 6 emphasize the distinction between program-wide uniqueness for callable and object entities versus translation-unit consistency for types, preventing subtle linkage errors in multi-file programs.

ODR-Usage Concept

The One Definition Rule (ODR) in C++ applies specifically to entities that are "odr-used" within a , meaning their s must be identical and appear exactly once (for non-inline entities) across all translation units where they are used. An entity is odr-used if it is referenced in a way that requires the or linker to access its full , rather than merely its or type information. This ensures that the 's semantics remain consistent despite separate compilation, preventing issues like multiple conflicting s from causing . Odr-use can be direct, such as when a is explicitly called (e.g., f()) or a variable's address is taken (e.g., &x), or indirect, such as through dispatch or implicit invocations like copy constructors in initialization. For variables, odr-use occurs if the variable is named by a potentially-evaluated expression that reads or writes its value, binds a to it (unless it's a compile-time ), or uses it in certain constant expressions requiring lvalue-to-rvalue . Functions are odr-used if named by a potentially-evaluated expression or , including non-pure functions or allocation/deallocation functions invoked by constructors and destructors. Additionally, entities like structured bindings (since ) or the implicit this pointer are odr-used in potentially-evaluated contexts. Not all references to an entity constitute odr-use. Pure declarations, such as forward declarations without any evaluation, do not trigger it. Type-only uses, like declaring a pointer to an incomplete type (e.g., T* p; without defining T), or tentative definitions (e.g., int x;) in contexts where the entity is not evaluated, are non-odr-uses. Unevaluated contexts, such as the operand of sizeof or alignof, or discarded-value expressions without lvalue-to-rvalue conversion (e.g., S::x; where the value is ignored), also avoid odr-use for variables usable in constant expressions. The precise rules for odr-use are detailed in sections [basic.def.odr]/4 through /10 of the . Paragraph 4 addresses functions named in overload resolution or explicit qualifications; paragraph 5 covers variables in potentially-evaluated expressions; paragraphs 6-7 extend to structured bindings and this; and paragraphs 8-9 specify function odr-use, including implicit cases for constructors and destructors. C++11 introduced refinements for lambdas, where closure types are considered equivalent across translation units if their lambda-expressions have matching token sequences, and for constexpr functions, which may require if named in potentially constant-evaluated contexts. Local entities have additional odr-usability constraints in nested scopes to prevent ill-formed programs. A key implication of the odr-use concept is that entities which are never odr-used—such as unused tentative definitions—can appear multiply across translation units without violating the ODR, as long as they are not linked or evaluated. This allows flexibility in header files for declarations that might not be utilized in every unit, but it underscores the need for careful design to avoid accidental odr-use triggering multiple definitions.

Exceptions and Special Cases

Inline Functions and Variables

The One Definition Rule (ODR) permits an exception for inline functions, allowing their definitions to appear in multiple translation units without violating the rule, provided all such definitions are identical in terms of token sequence, name lookup results, and language linkage. This exception facilitates the inclusion of inline function definitions in header files, which are then incorporated into every translation unit that includes the header. In contrast to non-inline functions, which must have exactly one definition across the entire program if odr-used, inline functions support this multiplicity to enable optimizations like inlining while maintaining program correctness. The mechanics of this exception rely on the inline specifier granting the function external linkage by default, with the linker responsible for selecting or merging the into a single instance during the linking phase. In practice, this is achieved through weak symbols or COMDAT sections (on platforms like Windows), ensuring that duplicate do not result in multiple instances but are unified into one, with shared behavior for function-local statics and types. An odr-use of an , such as a call or taking its address, requires its to be present in every translation unit where it occurs, but the program behaves as if there is only one if the ODR conditions are met. Introduced in C++17, the inline specifier was extended to variables with static storage duration, allowing multiple identical definitions of inline variables across translation units, similar to inline functions. This feature is particularly useful for header-only libraries, enabling declarations like inline static const int x = 5; without requiring external declarations or risking ODR violations. Inline variables must have external linkage (default for namespace-scope inline const variables) and identical initializers; their initialization is deferred until the first odr-use in the program. Key requirements for compliance include ensuring that all definitions are lexically identical and that the inline entity is odr-used consistently, though it must ultimately behave as a single definition per program. In header-only scenarios, avoiding odr-use within the header itself prevents the need for a single program-wide definition. However, if definitions differ—even subtly, such as in default arguments or constant values—the program exhibits undefined behavior, with no requirement for diagnostics from the compiler or linker. This exception does not apply to class types, which follow separate ODR rules for their definitions.

Templates and Explicit Specializations

In C++, the (ODR) applies to by permitting a single definition of a in each translation unit, provided that all such definitions across multiple translation units are . requires that the definitions consist of the identical sequence of and that name lookup within each definition resolves to the same entities, ensuring consistent behavior during compilation and linking. This rule extends to both and , where the primary 's definition must match exactly in form and semantics wherever it appears, typically through inclusion of the same header file. Template instantiation under the ODR involves implicit and explicit mechanisms, each with specific constraints. For implicit instantiations, the generates the from the primary (or the most specialized partial specialization if applicable) in each translation unit where the template is odr-used, relying on the equivalent primary to avoid discrepancies. Explicit specializations, however, are generated manually and must adhere to stricter rules: if defined in multiple translation units, they require identical definitions to maintain equivalence, but in practice, they are often declared in headers and defined once to prevent violations. Partial specializations follow the template equivalence model, allowing one per translation unit with the condition that all versions across units match in token sequence and name resolution; the selects the most specialized matching partial specialization during , falling back to the primary if none apply. Explicit specializations of templates are treated as non-template entities under the ODR, meaning there can be only one definition of such a specialization in the entire program, regardless of translation units. Unlike primary templates, explicit specializations cannot be implicitly instantiated and must be defined after the primary template's declaration but before their first odr-use to suppress unwanted implicit generation. They do not inherit inline, constexpr, or similar qualifiers from the primary template; instead, these attributes are determined solely by the specialization's own declaration—for instance, an explicit specialization can be marked inline to allow multiple definitions in different translation units, but without this, duplication leads to undefined behavior. For example, the following code demonstrates a valid explicit specialization:
template<typename T> void f(T) { /* primary template body */ }
template<> void f(int) { /* explicit specialization; defined once program-wide */ }
If this specialization were defined differently in another translation unit, it would violate the ODR. Introduced in C++20, modules impose stricter ODR enforcement for templates and their specializations through module interface units, which serve as the unique point of definition for exported entities. Unlike traditional header inclusions that can lead to multiple equivalent definitions, module imports reference a single module interface unit per named module, preventing inadvertent duplication and ensuring templates are instantiated from a unified source. This modular approach attaches template definitions to their module's linkage, making redefinitions across different modules ill-formed and thus enhancing ODR compliance by design. For instance, a template defined in a module interface unit is exported once and imported without redefinition in consuming modules, avoiding the token equivalence checks required in pre-C++20 header-based systems. Equivalence issues in templates often arise from subtle differences in name lookup across translation units, even when token sequences appear identical, potentially causing . For example, if a dependent name in a resolves to different entities due to varying enclosing scopes or macro expansions in different units, the resulting may differ, violating ODR despite superficial similarity. Such problems are particularly prevalent in templates with dependent names, where lookup occurs at the point of rather than , amplifying the risk if the primary are not perfectly synchronized. To mitigate this, best practices include centralizing in a single header and using forward declarations judiciously, ensuring consistent name resolution program-wide.

Static Const Data Members

In C++, static constant data members of a class are subject to the One Definition Rule (ODR), which requires that they have at most one definition across the entire program if odr-used, while allowing identical declarations in multiple translation units. For static const members, the rules for definitions have evolved across C++ standards to balance usability and linkage requirements. Prior to C++11, static const integral or enumeration type data members could be initialized and effectively defined in-class without an out-of-class definition, provided they were only used in constant expressions and not odr-used in ways that required storage, such as taking their address or using them in non-constant contexts. This in-class definition was sufficient because such members did not require separate linkage; however, if odr-used (e.g., via a pointer or reference beyond constant evaluation), an explicit out-of-class definition in exactly one translation unit was mandatory to provide the storage, ensuring ODR compliance. With , the rules tightened for broader applicability: all static data members, including const ones, require an out-of-class if odr-used, regardless of type, as in-class initializers alone do not constitute a full for linkage purposes unless the member is constexpr (introduced in ). A constexpr static data member must be initialized in-class with a constant expression, and this serves as its , implicitly making it inline and exempt from needing an out-of-class counterpart, even if odr-used. For non-constexpr static const members, odr-use—such as reading the value in a non-constant context or passing its address—triggers the need for a single out-of-class (e.g., const int ClassName::memberName;) in one translation unit, while identical in-class declarations are permitted elsewhere to avoid multiple definitions. Failure to provide this can lead to linker errors or if the member is odr-used without a . C++17 further simplified handling with the inline static specifier, allowing static data members (const or otherwise) to be defined in-class and included in every translation unit where the class is defined, as long as all such definitions are identical in content and form. This extension to static const members eliminates the previous requirement for out-of-class s in most cases, treating them like inline functions under the ODR: multiple identical definitions are merged at link time into one. However, the in-class initializer still only counts as a definition if the member is odr-used; otherwise, no explicit definition is needed at all. This change reduces common pitfalls in libraries but maintains ODR by prohibiting differing definitions across units. Under the ODR, multiple in-class declarations of a static const member are permissible if identical, but odr-use demands exactly one program-wide to avoid , such as when definitions differ between a header and a source file, potentially causing linker conflicts or multiple symbol instances. For instance, consider a with static const [int](/page/INT) value = [42](/page/42); in its header; if odr-used like const [int](/page/INT)* ptr = &Class::value;, a pre-C++17 program requires an out-of-class const [int](/page/INT) Class::value; in one .cpp file, or the linker may fail to resolve the symbol. In C++17, adding inline (e.g., inline static const [int](/page/INT) value = [42](/page/42);) resolves this by allowing the in-class to suffice everywhere, ensuring a single effective post-linking.

Violations and Consequences

Detectable Violations

Detectable violations of the One Definition Rule occur within a single translation unit, where the can identify and diagnose multiple or conflicting definitions of the same definable item during the process. According to the , no translation unit shall contain more than one definition of any definable item, such as a , , type, type, or ; violations of this intra-unit constraint render the program ill-formed, requiring the to issue a diagnostic. This mandate ensures that basic consistency is enforced at the stage, preventing obvious errors before linking. Common detectable cases include duplicate definitions of or non-identical within the same translation unit. For example, declaring and defining a type twice with differing members, such as an empty struct followed by one containing an member, results in a due to the conflicting violating the single- requirement. Similarly, providing multiple bodies for a non- or differing implementations for an in one unit triggers a diagnostic, as the standard prohibits such multiplicity and mandates identical semantics where applicable. For odr-used variables or lacking a in the translation unit where they are required, the may also detect and report the issue if the usage demands a complete at , though the standard does not always require a diagnostic for missing across the entire program. Compilers typically respond to these violations with errors for clear conflicts, such as mismatched types or multiple non-inline definitions, and may issue warnings for subtler issues like potential odr-usage without resolution. The standard introduces enhanced module-specific checks, mandating diagnostics for ODR mismatches only when a definable item is attached to a named and a definition is reachable at the point of , thereby improving detectability in modular codebases. Tools like include guards in header files help prevent accidental multiple inclusions that could lead to intra-unit violations, but they do not mitigate inter-unit ODR issues or excuse non-compliance with the rule's broader requirements.

Undetectable Violations and Undefined Behavior

Undetectable violations of the One Definition Rule (ODR) occur when non-identical definitions of entities such as functions, variables, or types exist across different translation units, and the or linker fails to detect the discrepancy. According to the , such programs are ill-formed, NDR (no diagnostic required), resulting in without any obligation for the implementation to issue a warning or error. For instance, if an has differing bodies in separate source files—such as one version performing a and another omitting it—the linker may select an arbitrary implementation, leading to semantically incorrect code that appears to link successfully. The consequences of these violations can manifest as program crashes, incorrect computational results, or subtle runtime bugs that are difficult to reproduce or debug, since the does not specify how the implementation must behave in the presence of such discrepancies. Even though only one may be linked into the final , the chosen one might not match the expectations from other units, potentially causing type mismatches or incompatible semantics. This unpredictability arises because traditional separates units, preventing cross-unit equivalence checks unless advanced techniques are employed. Modern compilers like and can detect some inter-unit ODR violations through Link-Time Optimization (LTO), which performs whole-program analysis by optimizing across object files. For example, enabling LTO with flags such as -flto allows these tools to compare definitions and report mismatches, though detection is not guaranteed for all cases, such as when symbols are weakly defined or in certain template instantiations. In C++20, the introduction of provides stronger safeguards against undetectable ODR violations. When a definable item is attached to a named interface and a definition is reachable via import, the standard requires a diagnostic if the definitions differ, thereby reducing the risk of silent in modular codebases. To mitigate these issues, best practices include declaring non-inline functions and variables in header files while providing their s in a single translation unit (e.g., a .cpp file), and restricting header-only to inline functions or templates where the ODR explicitly permits multiple identical instances. This approach ensures consistency across units without relying on optional features.

Practical Examples

Compliant Multiple Definitions

The One Definition Rule (ODR) permits multiple definitions of certain entities across translation units provided they are identical in token sequence, name lookup, and other specified attributes, ensuring no observable difference in program behavior. This exemption applies to , , and specific static members, allowing their definitions in header files included in multiple source files without violating the rule, as the linker merges identical copies.

Example 1: Inline Function in Header

An can be defined identically in a header file and included across multiple translation units; the generates code in each unit, but the linker discards duplicates during final linking. For instance, consider a header utils.h:
cpp
inline int add(int a, int b) {
    return a + b;
}
This header is included in file1.cpp and file2.cpp, both using add(1, 2). Each translation unit sees the same , satisfying ODR requirements for identical tokens and linkage. The resulting executable contains a single merged instance.

Example 2: Template Class in Header

Template definitions, including classes, may appear in headers and be instantiated equivalently in different translation units, as templates are not instantiated until used and multiple identical definitions are permitted if they match exactly. Consider a header vector.h:
cpp
template<typename T>
class SimpleVector {
    T data[10];
public:
    T& operator[](size_t i) { return data[i]; }
};
Included in main1.cpp and main2.cpp, instantiations like SimpleVector<int> v; produce matching code in each unit, complying with ODR via identical template definitions. No separate source file is needed, as the compiler handles instantiation per use.

Example 3: C++17 Inline Static Variable

In , static data members declared inline can be defined directly in the class within a header, allowing multiple inclusions without an out-of-class definition, as the linker merges them like inline functions. For example, in config.h:
cpp
struct Config {
    static inline int maxSize = 100;
};
When config.h is included in multiple .cpp files accessing Config::maxSize, the identical initializer ensures ODR compliance, avoiding multiple errors. This simplifies header-only libraries by eliminating explicit definitions.

Example 4: Pre-C++11 Static Const Integral In-Class

Prior to , static constant integral data members could be initialized in-class without an out-of-class definition if not odr-used (e.g., only as constant expressions in unevaluated contexts), permitting multiple identical declarations across units. In a header constants.h:
cpp
class Limits {
public:
    static const int bufferSize = 1024;
};
Included in several .cpp files, bufferSize is treated as a compile-time constant with no storage allocation if not odr-used (e.g., via sizeof or template arguments), thus no ODR violation occurs despite multiple "definitions." Odr-use, like taking its address, requires an out-of-class definition in one unit.

Example 5: Module Interface in C++20

In C++20, modules allow exporting definitions from a module interface unit (MIU), where entities like inline functions or variables can be defined once and across translation units without duplication, respecting ODR through module reachability rules. For example, a module interface utils.ixx:
cpp
export module Utils;

export inline int add(int a, int b) {
    return a + b;
}
This is compiled into a module interface unit, then in file1.cpp and file2.cpp via import Utils;, both using add(1, 2). The definition is shared via the module, ensuring a single instance without token sequence checks across traditional headers, as private module fragments (PMF) can contain non-exported differing details without ODR violation. This promotes modular code with stricter linkage isolation compared to headers.

Non-Compliant Cases and Side Effects

One common non-compliant case arises when a non-inline is defined directly in a header file that is included across multiple translation units, resulting in multiple definitions that violate the ODR and typically trigger a linker error. For instance, consider a header file utils.h containing:
cpp
void process(int value) {
    // Implementation here
    return value * 2;
}
If utils.h is included in both main.cpp and helper.cpp, the linker will detect duplicate symbols for process, refusing to produce an and reporting an error such as "multiple definition of 'process'". This detectable violation ensures the issue is caught at link time, preventing deployment of faulty . Another violation occurs when class definitions differ subtly across translation units, leading to type mismatches and at runtime due to incompatible layouts, such as varying member offsets. For example, in a.cpp:
cpp
struct S { [int](/page/INT) a; };
And in b.cpp:
cpp
[class](/page/Class) S { public: [int](/page/INT) a; };
Although both declare a type named S with an int member, the differing token sequences (e.g., struct vs. class, absence of access specifier) mean they are not the same entity, violating the ODR. This can cause buffer overruns or misaligned accesses when objects are passed between units, as the compiler assumes identical layouts. Prior to , odr-using a static const data member without an out-of-class definition also breaches the ODR, often manifesting as a linker error. Consider:
cpp
class Config {
public:
    static const int MAX_SIZE = 100;
};

int main() {
    int* ptr = new int[Config::MAX_SIZE];  // ODR-use via array size
    // ...
}
Here, MAX_SIZE requires a namespace-scope like const int Config::MAX_SIZE; in a single source file; omitting it leads to unresolved symbols at link time if the member is odr-used (e.g., for its address or in constant expressions). Post-, in-class initialization suffices for integral types, but pre- codebases must provide explicit definitions to comply. Template specializations that differ between translation units represent a subtle violation, potentially causing incorrect instantiations and without immediate linker feedback. For example, a header declares template<typename T> void func(T) { /* generic */ }, but unit1.[cpp](/page/CPP) adds a specialization:
cpp
template<> void func(int) { /* version A */ }
While unit2.[cpp](/page/CPP) provides:
cpp
template<> void func(int) { /* version B, differing body */ }
The compiler may instantiate the wrong version in one unit, leading to mismatched behavior when linking, such as invoking unintended logic. This undetectable case ties into broader from ODR violations, as detailed in related sections. Such non-compliant cases can produce severe side effects, including runtime crashes from address mismatches (e.g., pointers to different class instances) or incorrect virtual tables causing failed dynamic dispatch. For instance, differing class layouts might corrupt vtable pointers, leading to segmentation faults during polymorphism. Compiler behaviors vary: GCC's -Wodr flag detects some class violations via vtable analysis, while MSVC may overlook them unless using tools like /LTCG, potentially allowing UB to surface only at runtime. These effects underscore the need for consistent definitions to avoid exploits like vptr attacks.

References

  1. [1]
    [basic.def.odr]
    ### Extracted One Definition Rule (ODR) Text
  2. [2]
  3. [3]
  4. [4]
    3.2 One definition rule [basic.def.odr]
    Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required.
  5. [5]
  6. [6]
  7. [7]
  8. [8]
  9. [9]
    Definitions and ODR (One Definition Rule) - cppreference.com
    Dec 19, 2024 · Only one definition of any variable, function, class type, enumeration type, concept(since C++20) or template is allowed in any one translation unit.
  10. [10]
    inline specifier - cppreference.com - C++ Reference
    Aug 14, 2024 · The inline specifier, when used in a function's decl-specifier-seq, declares the function to be an inline function.
  11. [11]
  12. [12]
  13. [13]
  14. [14]
  15. [15]
  16. [16]
  17. [17]
  18. [18]
    ODR violation detection - MaskRay
    Nov 13, 2022 · This article describes how to detect C++ One Definition Rule (ODR) violations. There are many good resources on the Internet about how ODR violations can ...
  19. [19]
  20. [20]
  21. [21]
    ODR violation if template is defined in multiple translation units for ...
    Aug 25, 2022 · The answer is yes, this is an ODR violation. To solve the original problem, a better solution is to declare the template specializations in the header.ODR violation with template specializations - c++ - Stack OverflowCan ODR be violated if a template definition is only ever instantiated ...More results from stackoverflow.com
  22. [22]
    The One-Definition Rule | Andrzej's C++ blog - WordPress.com
    Nov 28, 2016 · The rule is simple: either a name is for everyone (and declared in a header file) or is translation-unit-local in an anonymous namespace. Some ...