3

I have a potential ODR violation happening in a large code base. It's a class template that switches behavior based on a #ifdef in different libraries, but the conflicting libs are likely using different instantiations of the template. As a simplified example:

// foo.h
#ifdef USE_DOUBLE
template <typename T>
struct foo { T* value; double x; };
#else
template <typename T>
struct foo { T* value; };
#endif

struct c;

// a_lib.cpp
#include "foo.h"
struct a { foo<a> m_a; };
struct a_helper { foo<c> m_c; };

// b_lib.cpp
#define USE_DOUBLE
struct b { foo<b> b; };
struct b_helper { foo<c> m_c; };
  1. I'm guessing that foo<a> and foo<b> don't have an ODR violation, correct?
  2. But that the differing definitions of foo<c> brought in by a_helper and b_helper just got incredibly sketchy, correct?

The catch is that I have this in a huge project. Also, it's likely (but not certain) that I have only the equivalent of non-overlapping a and b, and not the problematic a_helper and b_helper. However, I can't really be sure.

I'm wondering if I can dodge this problem by changing foo to an alias template:

template <typename T>
struct foo_double_impl { T* value; double x; };

template <typename T>
struct foo_impl { T* value; };

#ifdef USE_DOUBLE
template <typename T>
using foo = foo_double_impl<T>;
#else
template <typename T>
using foo = foo_impl<T>;
#endif
  1. Now, instead of two differing definitions of foo, we now have a definition of foo_impl and foo_double_impl. Does this resolve the ODR violation? Or does the ODR violation persist because there are two different alias templates of foo?
Ryan Shepherd
  • 755
  • 4
  • 11

1 Answers1

2

The ODR is specified rather lengthily in the standard that boils down to saying you must

  • Have exactly one definition of a non-inline function or variable
  • Have at least one definition of everything else used. The multitudes of definitions must be exactly the same, including
    • Have the same sequence of tokens
    • Have the sequence of tokens mean the same thing
    • Have all the lookup of the corresponding tokens find the same things

To sum up, they really really have to be identical in all possible manners.

In all your scenarios, foo violates ODR by having a differing sequence of tokens.


The best fix without altering the libraries are inline namespaces

#ifdef USE_DOUBLE
inline
#endif
namespace D {
    template <typename T>
    struct foo { T* value; double x; };
} 

#ifndef USE_DOUBLE
inline
#endif
namespace ND {
    template <typename T>
    struct foo { T* value; };
}
Passer By
  • 19,325
  • 6
  • 49
  • 96