5

Below is a program that completely demonstrates the problem I'm seeing.

First, I start with an object that is defined using a grouping of other types, I started using a std::tuple<> to manage the grouping.

template <typename> class object;
template <typename... Rs> class object<std::tuple<Rs...> > {
};

I am intending these objects to be capable of having the type void scattered in the "pack". I am already aware of being unable to "instantiate" a tuple of this type (see Void type in std::tuple)

I want to pass these objects around, perhaps copy/move them... none of their data members are a tuple of these types. In fact, I can reproduce the problem using the empty object definition above.

I can make it work, using something like:

template <typename... Rs> struct TGrp {};

template <typename> class object;
template <typename... Rs> class object<TGrp<Rs...> > {
};

These types of "grouping" structs are used frequenly in variadic recursion, and they are meant to never get created/used. Just to group template args.

However, I "want" the signature of the 'object' to be made up of "user expected" types/names.

Basically, I was experimenting with any possible way of passing one of these objects around when std::tuple is used to "group", and could only find one way: auto lambdas.

Can anybody explain:

  1. why the "auto" lambda's can work for this?

    something about delayed template deduction? like the diff b/w "auto" and "decltype(auto)"?

  2. how to "design" a function parameter to accept one of these objects.

-- thanks to you all for any insights on this oddity

Example:

#include <tuple>
#include <iostream>

#define GRP std::tuple      // IF 'tuple' used:  compile error where noted below
//#define GRP TGrp          // if THIS is used:  all works, and TGrp() is never constructed


// Grouping mechanism
template <typename... Ts> struct TGrp {
    TGrp() {
        std::cout << "Never printed message\n";
    }
};


// MAIN OBJECT (empty for forum question)
template <typename> class object;
template <typename... Rs> class object<GRP<Rs...> > {
};



// Regular function                  (does NOT work)
void takeobj(object<GRP<void> >& obj) { (void)obj; }

// Lambda - taking anything...       (only thing I could make WORK)
auto takeobj_lambda = [](auto obj) { (void)obj; };

// Template func - taking anything   (does NOT work)
template <typename T> void takeobj_templ_norm(T obj) { (void)obj; }
template <typename T> void takeobj_templ_clref(const T& obj) { (void)obj; }
template <typename T> void takeobj_templ_lref(T& obj) { (void)obj; }
template <typename T> void takeobj_templ_rref(T&& obj) { (void)obj; }


int main()
{
    object<GRP<void> > oval;

    //takeobj(oval);                  // <--    causes compile error

    takeobj_lambda(oval); // works

    //takeobj_templ_norm(oval);       // <--    also error
    //takeobj_templ_clref(oval);      // <--    also error
    //takeobj_templ_lref(oval);       // <--    also error
    //takeobj_templ_rref(oval);       // <--    also error
    return 0;
}

Edit: adding a trimmed down reproduction:

#include <tuple>


// MAIN OBJECT (empty for forum question)
template <typename> class object;
template <typename... Rs> class object<std::tuple<Rs...> > {
};

// Regular function                  (does NOT work)
void takeobj(object<std::tuple<void> >& obj) { (void)obj; }

// Lambda - taking anything...       (only thing I could make WORK)
auto takeobj_lambda = [](auto obj) { (void)obj; };


int main()
{
    object<std::tuple<void> > oval;

    //takeobj(oval);                  // <--    causes compile error
    takeobj_lambda(oval); // works

    return 0;
}
Community
  • 1
  • 1
CrashNeb
  • 394
  • 3
  • 12
  • 3
    [Simpler code to reproduce](http://goo.gl/i1xlK6) – M.M Jan 27 '16 at 04:08
  • @M.M I'm new to posting here, and thought you were asking for a better sample... then I clicked the link. Cool interactive page. And, yes that shorter example is what I'm seeing. – CrashNeb Jan 27 '16 at 04:35
  • Also: I am using Visual Studio 2015, so using the page from M.M i was able to see the "better" Clang error messages. ;) Still though, why do the lambda's (internally generated) class templates manage this and the "user built" templates cannot? – CrashNeb Jan 27 '16 at 04:35

1 Answers1

6

std::tuple<void> is an associated class of object<std::tuple<void>>, and so in an unqualified call in which argument-dependent lookup is performed, std::tuple<void> is instantiated to look for any friend functions that might have been defined inline. This instantiation causes an error.

Argument-dependent lookup is not performed if the thing being called doesn't name a function or function template; hence using a lambda works - takeobj_lambda is an object.

If you use either a qualified call (::takeobj(oval)), or parenthesize takeobj ((takeobj)(oval)), then the code compiles. Both of these disable ADL.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • Oh man (can't imagine figuring that out)... thank you very much!. It was just 3 weeks ago I had come across this answer [When do extra parentheses...](http://stackoverflow.com/a/24116818/5844631), bookmarked it, and tried to memorize it. But alas, I had "moved on" and would have spent unknown amounts of time while not making the connection that ADL was the problem here. – CrashNeb Jan 31 '16 at 18:39