1

Basically this is a question about semantics. I'm using Cereal library for (de)serialization in C++, and found its coding style interesting:

cereal::PortableBinaryInputArchive ar(instream);
int out;
ar(out);
// int is successfully deserialized from input stream here.

The tricky part is that I do not pass in the "out" by reference and ar() can still modify its value. Well in fact, the author just overrides the operator "()". And I found the corresponding lines in the source files.

OutputArchive & operator=( OutputArchive const & ) = delete;

  //! Serializes all passed in data
  /*! This is the primary interface for serializing data with an archive */
  template <class ... Types> inline
  ArchiveType & operator()( Types && ... args )
  {
    self->process( std::forward<Types>( args )... );
    return *self;
  }

I'm quite at a loss, especially the first line ("= delete") and things regarding "std::forward( args )...". I only saw some cases in which macros like va_arg are used and it's the first time that I've encountered something like this. Besides, what does "&&" stand for? Could anyone throw some light upon it?

Isilmë O.
  • 1,668
  • 3
  • 23
  • 35
  • 3
    `( Types && ... args )` is passing by reference, not by value. `Types &&` means that the parameters are references to `Types`. – M.M Jun 05 '15 at 09:11
  • This will resolve to the equivalent of `self->process(out);` so you should also check `process` to see whether it takes arguments by value or by reference. (Presumably it takes by reference , if it modifies the variable passed in) – M.M Jun 05 '15 at 09:13
  • 1
    search "perfect forwarding" to read about the language constructs used in this `operator()`. – M.M Jun 05 '15 at 09:16
  • and "universal references" in addition to PF – ixSci Jun 05 '15 at 09:17
  • 2
    This first line (`=delete`) has nothing to do with the `operator()`. That `=delete` line is telling the compiler to not generate an assignment (`=`) operator for the archive class. – Persixty Jun 05 '15 at 09:18
  • @DanAllen Amazing. Is that part of the standard? I haven't seen that before... :o – Isilmë O. Jun 05 '15 at 09:25
  • @MattMcNabb Just found a quite useful link about "&&": https://msdn.microsoft.com/en-us/library/dd293668.aspx Thanks for you hint for that. – Isilmë O. Jun 05 '15 at 09:26
  • @IsilmëO. Yes. C++11. See http://en.cppreference.com/w/cpp/language/as_operator. Before that you would "disable" them by making them `private`. However `delete` makes the purpose clearer (and improves error messages) and also avoids their use even in the class member implementations. – Persixty Jun 05 '15 at 09:30
  • Looks like you are a C programmer, and you've been exposed to too much C++ at once. To be clear, passing what looks like a value as a reference is VERY common in C++, and you don't have to do any of that confusing magic. Just define a function `void f(int & input);` and that function will receive `input` by reference even though you'll be calling `f` as `f(some_variable)` which would appear to be a pass-by-value to a C programmer. So, tune your eyes while reading C++ code. – enobayram Jun 05 '15 at 10:31

1 Answers1

4

I'm quite at a loss, especially the first line ("= delete")

The "= delete" effectively ensures that operator= (the assignment operator...) cannot be called, and that the default (assignment operator) shall not be generated. It is the same making operator= private, and not providing a definition. This operator can also be used for normal functions, in which case it's use is prohibited similarly (Refer c++ 11 standard, section 8.4.3):

struct B
{
  void foo(){}
};

struct D : B
{
  void foo() = delete;
};

int main() {
    D().foo(); //Fails to compile here - deliberate!!!
    return 0;
}

Note that the type on which foo is called is important. It could still be called on the base type, just like one could still slice even though derived assignment is prohibited (see example below):

struct B{};

struct D : B
{
  D& operator=(const D&) = delete;    
};

int main() {
    B b;
    D d1, d2;
    b = d1; //Compiles fine - slices
    d1 = d2; //Fails to compile...
    return 0;
}

and things regarding "std::forward( args )...".

std::forward allows perfect forwarding of arguments (i.e the types of arguments wrt to r/l valueness and modifiers don't change (Refer).

I only saw some cases in which macros like va_arg are used and it's the first time that I've encountered something like this.

template <class ... Types>
void foo( Types&& ...);

The ... in this context is referred to as variadic templates (google).

Besides, what does "&&" stand for? Could anyone throw some light upon it?

&& stands for either rvalue reference, or universal reference, depending on context. In this case it's a universal reference (Scott Meyers has a good article on universal references here).

EDIT: Universal references are correctly called forwarding references now (n4164).

Community
  • 1
  • 1
Werner Erasmus
  • 3,988
  • 17
  • 31
  • 2
    So "OutputArchive & operator=( OutputArchive const & )" is the basic value assignment and "= delete" just deletes it? Is that right? – Isilmë O. Jun 05 '15 at 09:27
  • This way, can I achieve something like: A derives from B, B has defined a custom operator(), I want to prohibit that in A, so I should write something like Type1 operator()(Type2 maybesomeparam) = delete or ()delete – Isilmë O. Jun 05 '15 at 09:33
  • I don't have the standard with me at present, therefore I can't comment. I know that it is only ever typically used in this context (therefore removing compiler generated functions, such as copy constructor/assignment) (by me, at least). – Werner Erasmus Jun 05 '15 at 09:41
  • 3
    @WernerErasmus any function may be deleted, this is sometimes useful in function overloading. You can write `void foo() = delete;` if you want. – M.M Jun 05 '15 at 09:43
  • 1
    N.B. universal references are correctly called _forwarding references_ now (see [N4164](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4164.pdf)) – Jonathan Wakely Jun 05 '15 at 09:53
  • @WernerErasmus, your latest edit isn't entirely correct, you can still call `B::foo()` by qualifying it so that you call the base function, not the deleted function that hides it: `D d; d.B::foo();` or because it's a non-virtual function: `D d; B& b = d; b.foo();` – Jonathan Wakely Jun 05 '15 at 09:55
  • @JonathanWakely, just like you can slice, even though derived assignment operator is prohibited... The point is that from derived context, you want to prohibit the calling. – Werner Erasmus Jun 05 '15 at 09:57