2

So I'm trying to implement small object optimization in a project of mine, but I'm running into a strange compiler error. Here's some simplified code that reproduces the issue:

#include <type_traits>

template<typename T>
class Wrapper {
    T thing;
public:
    Wrapper(T&& thing) {
        // ...
    }
};

class Test {
    static const size_t PADDING_SIZE = 64;
public:
    template<
        typename T, 
        std::enable_if_t<sizeof(Wrapper<std::decay_t<T>>) <= PADDING_SIZE, int> = 0
        // Error on this line ^
    >
    Test(T&& thing) {
        new (padding) Wrapper<std::decay_t<T>>(std::forward<T>(thing));
    }

    char padding[PADDING_SIZE];
};

int main() {
    auto t = Test(0.0f);
}

Basically, I need to take an arbitrary object, put it in a wrapper, and instantiate an instance of the wrapper in the padding, but I need to use one wrapper for types that can fit in the padding and a different one for types that are too large (one of the wrappers stores the object in place while the other allocates external space for it). And obviously I'd like to support perfect forwarding.

Unfortunately, VS2017 gives me the following compiler error: error C2027: use of undefined type 'Wrapper<decay<_Ty>::type>'. I can compile it just fine with Wrapper<T> rather than Wrapper<std::decay_t<T>>, but I think I need to use the decayed type. Clang compiles it fine as is.

So what's the problem here? I'm a bit stuck.

nzerbzer
  • 105
  • 6

1 Answers1

3

Possibly a bug in the VS compiler.

I can get it to compile with a slightly different form of the sfinae condition, using a default type instead of a default value:

#include <type_traits>
#include <new>

template<typename T>
class Wrapper {
    T thing;
public:
    Wrapper(T&& ) {
        // ...
    }
};

class Test {
    static const size_t PADDING_SIZE = 64;
public:
    template<
        typename T, 
        class = std::enable_if_t<sizeof(Wrapper<std::decay_t<T>>) <= PADDING_SIZE>
    >
    Test(T&& thing) {
        new (padding) Wrapper<std::decay_t<T>>(std::forward<T>(thing));
    }

    char padding[PADDING_SIZE];
};

int main() {
    auto t = Test(0.0f);
}

No real explanation of why this should work better, it's just the form I usually use.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • That compiles, but when I add the other constructor (with `std::enable_if_t>) > PADDING_SIZE`) I get `error C2535: 'Test::Test(T &&)': member function already defined or declared`. – nzerbzer Dec 07 '17 at 05:13
  • That's a different problem. You cannot have two functions that just differ in the default value for a template parameter. https://stackoverflow.com/a/38540922/597607 – Bo Persson Dec 07 '17 at 05:32
  • You're right, I fixed it by adding a dummy template parameter to one of the signatures. It's still causing a compiler error in my project, but the example I gave in the question compiles now, so I guess it's a separate issue. – nzerbzer Dec 07 '17 at 05:49