Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

C++ – why can't the compiler deduce the types for the function template containing constructor parameters?

I want to use std::aligned_storage_t to decouple allocation and construction, so I write two little helpers to help me do this

#include <type_traits>
template <typename T>
using Uninitialized = std::aligned_storage_t<sizeof(T), alignof(T)>;

template <typename T, typename... Args>
auto get_initialized(Uninitialized<T>& block, Args... args) -> T&
    requires(
        // If we allow type with default constructor, then it would be ambiguous
        // if the caller wants initialization or simply getting reference.
        !std::is_default_constructible_v<T> &&
        (sizeof...(Args) == 0 || std::is_constructible_v<T, Args...>)
    )
{
    T* p = (T*) &block;
    if constexpr (sizeof...(Args) > 0)
        new (p) T{args...};
    return *p;
}

And I tested it with

class Obj {
public:
    Obj() = delete;
    Obj(const char* s) noexcept : m_str(s) {}

    void print() noexcept {
        std::cout << m_str << std::endl;
    }
private:
    const char* m_str = nullptr;
};

int main() {
    Uninitialized<Obj> obj{};
    const char* s = "Test";
    Obj& o = get_initialized(obj, s);
    o.print();
}

And it failed. I’ve tried clang, gcc and msvc, and they all complained about "no matching function call". Complaint from clang

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

<source>:37:14: error: no matching function for call to 'get_initialized'
    Obj& o = get_initialized(obj, s);
             ^~~~~~~~~~~~~~~

Of course, simply add <Obj> the code would work fine. But I just want to save some typing here. I found output from gcc providing some insight here

<source>:37:29: error: no matching function for call to 'get_initialized(Uninitialized<Obj>&, const char*&)'
   37 |     Obj& o = get_initialized(obj, s);
      |              ~~~~~~~~~~~~~~~^~~~~~~~

So I guess the problem relates to the type of the parameters of constructor, and I’m also quite curious here why would the type of s would be deduced to char*& while it’s just a 8 byte data and can be copied without extra cost. So is there a way to enforce the parameter to be copied, just like std::ref or std::cref?

>Solution :

Uninitialized<T> is an alias…

So your template is actually

using Uninitialized = ;

template <typename T, typename... Args>
auto get_initialized(std::aligned_storage_t<sizeof(T), alignof(T)>& block, Args... args) -> T&

T is not deducible here (any class with same sizeof/alignof would be valid).

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading