rkjnsn
rkjnsn

Reputation: 985

Prevent ADL on template parameters

I currently have a class template that takes a series of types. Each type may need to be instantiated with the class, itself. What I currently have is something like this:

template <typename... Types>
struct TypeList; // Not defined

struct Placeholder; // Not defined

template <typename Types, typename AnotherType = Default>
class MyClass
{
    // ...
};

You can then use it like this:

typedef MyClass<TypeList<Container1<Placeholder>, Container2<std::string,
        Placeholder>, OtherType>, OptionalType> MyTypedef;
MyTypedef my_object;

MyClass will replace appearances of Placeholder with itself, use the resulting types, and all is well.

The problem occurs when I try to do something like either of these:

MyTypedef *my_ptr = &my_object;
my_free_function(my_object);

Both of these cause a compiler error, because the compiler tries to instantiate Container1<Placeholder> and Container2<std::string, Placeholder> to do argument dependent lookup (ADL), and this instantiation with Placeholder, itself, fails.

I know it is possible to avoid ADL by doing, e.g.,

MyTypedef *my_ptr = std::addressof(my_object);
(my_free_function)(my_object);

However, I don't want to burden the user of MyClass with having to constantly suppress ADL. Is there another, straightforward way to have the user provide a list of types without those types being used for ADL?

Upvotes: 5

Views: 756

Answers (1)

rkjnsn
rkjnsn

Reputation: 985

Okay, I got everything working. The trick was to use a dependent type instead of using a template, directly. My final solution was to define TypeList as follows:

template <typename... Types>
struct TypeList
{
private:
    struct Holder
    {
    private:
        typedef TypeList<Types...> InnerTypeList;
        template <typename Types, typename AnotherType>
        friend class MyClass;
    };
public:
    typedef Holder type;
};

Then, the users of MyClass can do

typedef MyClass<TypeList<Container1<Placeholder>, Container2<std::string,
        Placeholder>::type, OtherType>, OptionalType> MyTypedef;
MyTypedef my_object;

Note the addition of '::type'

Finally, in MyClass, I replaced

typedef typename SubstituteType<Types, Placeholder, MyClass>::type InternalTypeList;

with

typedef typename SubstituteType<Types::InnerTypeList, Placeholder, MyClass>::type
        InternalTypeList;

giving me the same type for InternalTypeList as before.

Because the dependent type Holder has no template parameters of it's own, the compiler doesn't have to instantiate the Placeholder types for ADL purposes, and everything works properly.

Upvotes: 1

Related Questions