Search code examples
c++templatesconstructorallocator

Conditionally allow supply of allocators to wrapped objects


I'm creating a container that can be templated on the string type it uses for its members, either it's an owning or non-owning string (std::string vs std::string_view). For the case of the non-owning string, I can't supply an allocator and hence I have to convince the compiler to write a constructor that doesn't take an alloctor. Here's my current approach but I have yet multiple problems to solve:

Demo

#include <iostream>
#include <string_view>
#include <memory>
#include <string>

template <typename T>
concept uses_allocator_general = requires {
    typename T::allocator_type;
};


template <typename StringType = std::string>
struct URL {
    using allocator_type = std::conditional_t<uses_allocator_general<StringType>,
        typename StringType::allocator_type, void>;

    URL(allocator_type allocator = {})
        : mystr{allocator}
    { }

    StringType mystr;
};

int main() {
    URL myurl;

    URL<std::string_view> myurl_view;
}
  1. This doesn't compile as StringType::allocator_type is evaluated before std::conditional_t. I would have to use some kind of indirection or SFINAE to retrieve the typedef conditionally.
  2. Even if it were to compile, can I just conditionally void-erase the parameter even if it takes a default parameter? I don't think this is possible. What alternatives do I have?
  3. Maybe there exists a more elegant solution for this problem that I don't know of?

Solution

  • You can provide two constraint constructors for the two cases respectively

    template <typename T>
    concept uses_allocator_general = requires {
        typename T::allocator_type;
    };
    
    template <typename StringType = std::string>
    struct URL {
        template<uses_allocator_general AllocStringType = StringType>
        URL(AllocStringType::allocator_type allocator = {})
            : mystr{allocator}
        { }
    
        URL() requires (!uses_allocator_general<StringType>) = default;
    
        StringType mystr;
    };
    

    Demo