2

I want to create a variadic perfect-forwarding make_shared<T> wrapper, but one which is SFINAEd on whether the constructor of T takes any non-const reference/pointer arguments. The idea is to have two wrappers, called construct and construct_nonconst, where when constructing a Foo(int& r) or Foo(int* r), one is obliged to use the latter. The purpose of this is so that when developers write classes whose constructors require non-const parameters, they can do so, but it clearly shows up at the call site that the construction may have local side-effects.

I looked at Implementing variadic type traits, and played with is_constructible (I was trying to convert my Args... parameter pack to all the const versions therein), but I can't quite seem to figure it out.

Desired outcome:

struct NonConst
{
  NonConst(int& uhOh)
  {
    uhOh = 2;
  }
};

struct Const
{
  Const(const int& noProblemo)
  {
    // ...
  }
};

struct ByValueStr
{
  ByValueStr(std::string noProblemo)
  {
    // ...
  }
};

int x = 5;
const int y = 5;
std::string s("foo")

auto nc1 = builder<NonConst>::construct(x); // this doesn't compile
auto nc2 = builder<NonConst>::construct_nonconst(x); // fine, but noticeable
auto c1 = builder<Const>::construct(x); // fine
auto c2 = builder<ByValueStr>::construct(s); // fine
Community
  • 1
  • 1
experquisite
  • 879
  • 5
  • 14
  • 1
    I wonder if this is really a good idea. A constructor might not use the pointer or reference to modify the referent, but a member function could modify it later. In that case you needlessly force people to write `construct_nonconst`. Unless it's intentional in this case too? – Brian Bi May 06 '15 at 01:05
  • Yes, the idea is that 99% of all the classes will be immutable and/or referential transparent and it should be very clear when this is not the case. – experquisite May 06 '15 at 01:13
  • I should also add that I tried doing this with just variadic `const Args&... ` but ran into problems with constructors taking strings using char literals. – experquisite May 06 '15 at 01:14
  • Wouldn't it be easier to add const to all lvalue arguments? – dyp May 06 '15 at 01:16
  • I am not sure, I tried to add const to my Args... while using `is_constructible` but it didn't appear to do anything. Maybe I am misunderstanding. Universal reference with perfect forwarding is perfect, I just want to turn it off if there are any non-const pointers or references (or smart pointers for that matter) – experquisite May 06 '15 at 01:31
  • 1
    I meant: `template T const& const_if_lvalue(T& t) { return t; } template T&& const_if_lvalue(T&& t) { return move(t); } template R construct(Args&&... args) { return R(const_if_lvalue(forward(args))...); }` – dyp May 06 '15 at 01:41
  • @dyp, that seems to work but only if I add an enable_if to the otherwise universal reference implementation of `const_if_lvalue`, per http://stackoverflow.com/questions/7748104/preventing-non-const-lvalues-from-resolving-to-rvalue-reference-instead-of-const , `template< class T, typename = typename std::enable_if<!std::is_reference::value>::type > T&& const_if_lvalue(T&& t) { return std::move(t); }` – experquisite May 06 '15 at 16:48
  • If you'd like to write that up as an answer, it works for me. – experquisite May 06 '15 at 16:48
  • @experquisite Did you use `template T const& const_if_lvalue(T const& t) { return t; }`? It is important that the function parameter is `T&`, not `T const&`. Which compiler did you use? – dyp May 06 '15 at 17:04
  • I used `T& t` but got ambiguous overload errors in gcc 4.8.2. – experquisite May 06 '15 at 18:06
  • @experquisite Interesting. Looks like a bug: it works in gcc 4.9 -- You can as well write it up yourself as an answer, and maybe explain how this helps you. I don't think I've understood your original issue entirely. – dyp May 06 '15 at 22:33

0 Answers0