0
template<typename T> class Group;
template<typename...U>
class Group<std::tuple<U...>> {
public:
    typedef std::tuple<U...> type;
    
    void emplace_back(U&...elem) {
        data_.emplace_back(elem...);
    }
    void emplace_back(U&&...elem) {
        data_.emplace_back(elem...);
    }
private:
    std::vector<type> data_;
};

int main() {
  Group<std::tuple<int, std::string>> g;
  int e1 = 10;
  std::string e2 = "elem2";
  g.emplace_back(e1, e2); //OK
  g.emplace_back(e1, "elem2"); //error, no instance of overloaded function
  return 0;
}

I need to create a template class that, in its partial specialization, uses a std::tuple to create a type list consisting of non-cvr (non-const, non-volatile, non-reference) ordinary types. The class should use a std::vector to store a series of elements of std::tuple type, where the element types in the std::tuple match the created type list. The example code has abstracted and simplified the problem, so the operation of creating a type list using std::tuple might seem nonsense.

When inserting elements into the std::vector, a non-template member function calls the std::vector::emplace_back() function. Since it's a non-template function, perfect forwarding of arguments cannot be achieved; either all arguments in the parameter pack are uniformly passed as lvalue references, or they are uniformly passed as rvalue references.

Now, I am wondering if there is a way to individually determine whether each argument is an lvalue reference or an rvalue reference and forward them accordingly to the std::vector::emplace_back() function.

I thought of changing Group::emplace_back() to a template function, which would allow perfect forwarding. However, this would lose the constraint of the class template's type parameter U on the function's arguments, so I prefer not to use this method.

3
  • 1
    Offtopic: IMHO using tuples which have fixed types only is a bad practice. This makes code harder to read and maintain. Regular struct gives you opportunity to name fields and this way express your intent.
    – Marek R
    Commented Jun 20 at 8:23
  • Yes just make a struct (std::pair/std::tuple are semantically meaningless constructs, only to be used in meta template programming). Also know that templates are (thankfully) NOT involved in implicit type conversions.Your code expects a std::string not a const char* for "elem2" and that is what your compiler is trying to tell you. (Can be solved at callsite : use string literals and "elem2"s). Commented Jun 20 at 8:48
  • @Marek R, I need to dynamically select several types from a fixed type to form a new type, struct does not support this operation. @Pepijn Kramer, Thanks for reminding me, I was careless. Actually, if I write std::string("elem2"), it will not find a matching function either.
    – soyokaze
    Commented Jun 20 at 14:26

1 Answer 1

4

You have two sane options here. Either a single non-template function where you move the elements:

void emplace_back(U ...elem)
{
    data_.emplace_back(std::move(elem)...);
}

Or a template one with perfect forwarding.

I thought of changing Group::emplace_back() to a template function, which would allow perfect forwarding. However, this would lose the constraint of the class template's type parameter U on the function's arguments, so I prefer not to use this method.

What stops you from manually writing the constraint?

template <typename ...P> requires (std::constructible_from<U, P &&> && ...)
void emplace_back(P &&... elem)
{
    data_.emplace_back(std::forward<P>(elem)...);
}
2
  • @DanielLangr Mhm, thanks. Commented Jun 20 at 10:40
  • Thanks for your answer. The problem that has been troubling me for a long time is solved. Actually, I wanted to solve the problem using C++'s syntax features or type traits, so I was reluctant to use concepts. However, thinking about it carefully, concepts are fundamentally inseparable from type traits in their concrete implementation.
    – soyokaze
    Commented Jun 20 at 14:37

Not the answer you're looking for? Browse other questions tagged or ask your own question.