Vous avez dit bizarre ?

Copyright 2017 olibre   CC BY-SA 4.0

le logo C++FRUG est consitué du drapeau de la francophonie avec C++ au centre

Pas besoin d’indiquer return pour main()

int main()
{
  // return 0 by default
}
> g++ main.cpp && ./a.out && echo $?
0
#include <iostream>

int main()
{
  std::cout << 007 << std::endl;
  std::cout << 042 << std::endl;
  std::cout << 123 << std::endl;
}
#include <iostream>

int main()
{
  std::cout << 007 << std::endl;
  std::cout << 042 << std::endl;
  std::cout << 123 << std::endl;
}
7
34
123
#include <iostream>

int main()
{
  std::cout << std::oct;  // s'applique partout

  std::cout << 007 << std::endl;
  std::cout << 042 << std::endl;
  std::cout << 123 << std::endl;
}
7
42
173

Temps parcouru par la lumière entre le Soleil et Jupiter

#include <iostream>
#include <chrono>
using namespace std::chrono_literals;

template <typename T>
auto answer(T t)
{
  // Light takes 43:07 from Sun to Jupiter
  return t ???-- 43:07;
}

int main()
{
  auto day = 24h;
  auto halfhour = 0.5h;
  std::cout <<"one day is "<< day.count() <<" hours\n"
            <<"1/2 hour is "<< halfhour.count() <<" hours\n"
            <<"the answer is "<< answer(day.count()) <<'\n';
}
one day is 24 hours
1/2 hour is 0.5 hours
the answer is 42

http://coliru.stacked-crooked.com/a/0f60ac9f5bef835a

Trigraph   ??-   ->   ~

#include <iostream>
#include <chrono>
using namespace std::chrono_literals;

template <typename T>
auto answer(T t)
{
  // Light takes 43:07 from Sun to Jupiter
  return t ?~- 43:07;
}

int main()
{
  auto day = 24h;
  auto halfhour = 0.5h;
  std::cout <<"one day is "<< day.count() <<" hours\n"
            <<"1/2 hour is "<< halfhour.count() <<" hours\n"
            <<"the answer is "<< answer(day.count()) <<'\n';
}
one day is 24 hours
1/2 hour is 0.5 hours
the answer is 42

Opérateur ternaire

#include <iostream>
#include <chrono>
using namespace std::chrono_literals;

template <typename T>
auto answer(T t)
{
  // Light takes 43:07 from Sun to Jupiter
  return t ? ~-43 : 07;
}

int main()
{
  auto day = 24h;
  auto halfhour = 0.5h;
  std::cout <<"one day is "<< day.count() <<" hours\n"
            <<"1/2 hour is "<< halfhour.count() <<" hours\n"
            <<"the answer is "<< answer(day.count()) <<'\n';
}
one day is 24 hours
1/2 hour is 0.5 hours
the answer is 42

Complément à un (ones’ complement) std::bit_not~1   ->   -2   (si signé)

#include <iostream>
#include <chrono>
using namespace std::chrono_literals;

template <typename T>
auto answer(T t)
{
  // Light takes 43:07 from Sun to Jupiter
  return t ? 42 : 07;
}

int main()
{
  auto day = 24h;
  auto halfhour = 0.5h;
  std::cout <<"one day is "<< day.count() <<" hours\n"
            <<"1/2 hour is "<< halfhour.count() <<" hours\n"
            <<"the answer is "<< answer(day.count()) <<'\n';
}
one day is 24 hours
1/2 hour is 0.5 hours
the answer is 42

Vous avez dit bizarre ?

#include <iostream>

int main()
{
  auto x = ("C++", '2017');
  std::cout << x << std::endl;
}

http://coliru.stacked-crooked.com/a/8ff0bbede46cd18f

Vous avez dit bizarre ?

#include <iostream>

int main()
{
  auto x = ("C++", '2017');
  std::cout << x << std::endl;
}
842019127

Opérateur virgule (Comma operator) wikipedia.org cppreference.com

#include <iostream>

int main()
{
  auto x = ("C++", '2017');
  std::cout << x << std::endl;
}
842019127

Sans l’opérateur virgule

#include <iostream>

int main()
{
  auto x = '2017';
  std::cout << x << std::endl;
}
842019127

Multi-character character constant

#include <iostream>

int main()
{
  int x = '2017';
  std::cout << x << std::endl;
}
842019127

Multi-character character constant are just characters

#include <iostream>

int main()
{
  int x = '2017';
  char* txt = reinterpret_cast<char*>(&x);
  std::cout << txt[3];
  std::cout << txt[2];
  std::cout << txt[1];
  std::cout << txt[0];
}
2017

Non mais !!??

#include <iostream>

template <bool B = true>
bool foo (bool b = B) {
  return !!b??!??!!!b;
}

template <bool B = false>
bool bar (bool b = B) {
  std::cout << !b??!??!!b;
}

int main() {
  !foo()??!??!bar();
}
1

http://coliru.stacked-crooked.com/a/3a7d316dd41cb1b3

Trigraphs   ??!   ->   |

#include <iostream>

template <bool B = true>
bool foo (bool b = B) {
  return !!b || !!b;     // !!b??!??!!!b
}

template <bool B = false>
bool bar (bool b = B) {
  std::cout << !b || b;  // !b??!??!!b
}

int main() {
  !foo() || bar();      // !foo()??!??!bar();
}
1

Simplifications

#include <iostream>

template <bool B = true>
bool foo (bool b = B) {
  return b;  // !!b || !!b
}

template <bool B = false>
bool bar (bool b = B) {
  std::cout << true;  // !b || b
}

int main() {
  if (foo()) bar();  // !foo() || bar();
}
1
#include <iostream>

int main()
{
  char a[] = "ciel + v";

  std::cout << 7 + a << 1[a] << 7[a]
     << 2[a] << 6[a] << 3[a] << 2[a]
     << 6[a] <<  *a  << 5[a] << a[5];
}
#include <iostream>

int main()
{
  char a[] = "ciel + v";

  std::cout << 7 + a << 1[a] << 7[a]
     << 2[a] << 6[a] << 3[a] << 2[a]
     << 6[a] <<  *a  << 5[a] << a[5];
}
vive le c++

Ternary as lvalue

(a ? b : c) = 42;
#include <iostream>

int main()
{
    int a, b, c;
    (a ? b : c) = 42;
    std::cout << a <<' '<< b <<' '<< c;
}
32765 42 0

URL in code

#include <iostream>

int main()
{
std::cout << '4';
http://www.cppfrug.org
std::cout << '2';
}

http://coliru.stacked-crooked.com/a/230fc215df0fa6d1

URL in code

#include <iostream>

int main()
{
std::cout << '4';
http://www.cppfrug.org
std::cout << '2';
}
42
#include <iostream>

int main()
{
std::cout << '4';
http://www.cppfrug.org
std::cout << '2';
goto http;
}
42222222222222222222222222222222...

Scope-declared types

#include <iostream>

int main()
{
  for (struct { int a; float b; } loop = { 1, 2.3 };
       loop.a < 5 || loop.b < 999;
       loop.a++, loop.b *= loop.a)

         std::cout << loop.a <<' '<< loop.b <<'\n';
}
1 2.3
2 4.6
3 13.8
4 55.2
5 276

Optionnellement bizarre

std::optional<bool> var(false);

if (var && var == false)
    std::cout << "lol";
#include <iostream>
#include <optional>

int main()
{
  std::optional<bool> var(false);

  if (var && var == false)
    std::cout << "lol";
}
lol
template<typename T>
void foo (T& t)
{
    t.bar<int>();
}

struct C
{
    template<typename T>
    void bar() { }
};

int main()
{
    C c;
    foo<C>(c);
}
> g++-7.2 -Wall -Wextra -pedantic main.cpp
main.cpp: In function 'void foo(T&)':
main.cpp:4:11: error: expected primary-expression before 'int'
      t.bar<int>();
            ^~~
main.cpp:4:11: error: expected ';' before 'int'
template<typename T>
void foo (T& t)
{
    t.bar<int>();
}

struct C
{
    template<typename T>
    void bar() { }
};

int main()
{
    C c;
    foo<C>(c);
}
> clang++-3.6 -Wall -Wextra -pedantic main.cpp
main.cpp:4:7: error: use 'template' keyword to treat 'bar' as a dependent template name
    t.bar<int>();
      ^
      template
template<typename T>
void foo (T& t)
{
    t.template bar<int>();  // Insérer template
}

struct C
{
    template<typename T>
    void bar() { }
};

int main()
{
    C c;
    foo<C>(c);
}

Passer le retour d’une fonction par paramètre

class C {};

void foo (C &) {}

C bar() { return C(); }

int main()
{
  foo( bar() );
}

GCC ne veut pas

class C {};

void foo (C &) {}

C bar() { return C(); }

int main()
{
  foo( bar() );
}
> g++-7.2 -Wall -Wextra main.cpp
main.cpp: In function 'int main()':
main.cpp:9:11: error: cannot bind non-const lvalue reference of type 'C&' to an rvalue of type 'C'
   foo( bar() );
        ~~~^~
main.cpp:3:6: note:   initializing argument 1 of 'void foo(C&)
 void foo (C &) {}
      ^~~

Clang non plus

class C {};

void foo (C &) {}

C bar() { return C(); }

int main()
{
  foo( bar() );
}
> clang++-3.8 -Wall -Wextra main.cpp
main.cpp:9:3: error: no matching function for call to 'foo'
  foo( bar() );
  ^~~
main.cpp:3:6: note: candidate function not viable: expects an l-value for 1st argument
void foo (C &) {}
     ^

Transformer une R-Value en L-Value

class C {};

void foo (C &) {}

C bar() { return C(); }

int main()
{
  foo( bar() );
}

Le standard C++ ne permet pas de transformer une R-Value en L-Value
(modifier un objet temporaire qui va tout de suite mourir)

class C {};

void foo (C &) {}

C bar() { return C(); }

int main()
{
  foo( bar() );
}
  1. La fonction foo() est appelée avec comme paramètre le retour de la fonction bar() c’est une unnamed variable (ou anonymous variable).
  2. Comme sa durée de vie s’arrête avec la fin de l’appel de foo(), c’est aussi un temporary object (ou temporary value).
  3. Mais foo(C&) attend un objet modifiable.
    (même s’il n’en fait rien, cet objet doit être modifiable.)
  4. Donc il n’y a pas d’intérêt à donner un objet temporaire à une fonction qui va le modifier.

Scott Meyers - More Effective C++

Consider this function:

// changes all chars in str to upper case
void uppercasify( std::string& str );

In the character-counting example, a char array passed to uppercasify() fails:
http://coliru.stacked-crooked.com/a/f324de25c709cb0e

char title[] = "Effective C++";
uppercasify(             title  );  // error #1
uppercasify( std::string(title) );  // error #2

error #1 No temporary is created to make the call succeed. Why not?

Suppose a temporary were created (error #2). Then the temporary would be passed to uppercasify(), which would modify the temporary so its characters were in upper case. But the actual argument to the function call — title — would not be affected; only the temporary std::string object generated from title would be changed.

Surely this is not what the programmer intended. That programmer passed title to uppercasify(), and that programmer expected title to be modified.

Implicit type conversion for references-to-non-const objects, then, would allow temporary objects to be changed when programmers expected non-temporary objects to be modified. That’s why the language prohibits the generation of temporaries for non-const reference parameters. Reference-to-const parameters don’t suffer from this problem, because such parameters, by virtue of being const, can’t be changed.

Contournenement C++03

struct C {};

void foo (const C &) {}  // const

C bar() { return C(); }

int main()
{
  foo( bar() );
}

Un autre contournenement C++03

struct C {};

void foo (C &) {}

C bar() { return C(); }

int main()
{
  C c;  // no temporary object
  foo( c = bar() );
}

Contournement C++11

#include <utility>  // std::move

struct C
{
    C &  ok() { return           *this;  }
    C && ko() { return std::move(*this); }
};

void foo (C &) {}

C bar() { return C(); }

int main()
{
    foo( bar().ok() );
    foo( bar().ko() );  // ERROR
}

Mais sans succès avec les ref-qualifier & et &&

#include <utility>  // std::move

struct C
{
    C &  ok() &  { return           *this;  }
    C && ko() && { return std::move(*this); }
};

void foo (C &) {}

C bar() { return C(); }

int main()
{
    foo( bar().ok() );  // ERROR
    foo( bar().ko() );  // ERROR
}

Avec la rvalue-reference (C++11)

struct C {};

void foo (C &&) {}  // non-const rvalue-reference

C bar() { return C(); }

int main()
{
  foo( bar() );
}

Que sont c1 et c2 ?

#include <iostream>

template<class T>
struct C
{
  C()  { std::cout <<"default ctor\n";            }
  C(T) { std::cout <<"ctor: "<< sizeof(T) <<'\n'; }
};

template <class T> C<T> c1;
template <class T> C<T> c2(2);

int main()
{
  auto a1 = c1<short>;
  auto a2 = c2<short>;
  auto a3 = c2<double>;
}

Que sont c1 et c2 ?

#include <iostream>

template<class T>
struct C
{
  C()  { std::cout <<"default ctor\n";            }
  C(T) { std::cout <<"ctor: "<< sizeof(T) <<'\n'; }
};

template <class T> C<T> c1;
template <class T> C<T> c2(2);

int main()
{
  auto a1 = c1<short>;
  auto a2 = c2<short>;
  auto a3 = c2<double>;
}
default ctor
ctor: 2
ctor: 8

c1 et c2 sont des variables template (C++14)

#include <iostream>

template<class T>
struct C
{
  C()  { std::cout <<"default ctor\n";            }
  C(T) { std::cout <<"ctor: "<< sizeof(T) <<'\n'; }
};

template <class T> C<T> c1;
template <class T> C<T> c2(2);

int main()
{
  auto a1 = c1<short>;
  auto a2 = c2<short>;
  auto a3 = c2<double>;
}
default ctor
ctor: 2
ctor: 8

À quoi sert les variable templates ?

Avant, pour avoir des “variables paramétrables” on avait deux contournements :

Quelle ligne ne compile pas ?

template <int X>
struct Base
{
    void foo() { }
};

template <int X>
struct Derive : Base<X>
{
    void bar() { foo(); }
};

http://coliru.stacked-crooked.com/a/daca4a7b7d8ef906

GCC

template <int X>
struct Base
{
    void foo() { }
};

template <int X>
struct Derive : Base<X>
{
    void bar() { foo(); }  // ERROR
};
> g++-7.2 -Wall -Wextra -pedantic main.cpp
main.cpp: In member function 'void Derive<X>::bar()':
main.cpp:10:18: error: there are no arguments to 'foo' that depend on a template parameter, so a declaration of 'foo' must be available [-fpermissive]
     void bar() { foo(); }
                  ^~~
main.cpp:10:18: note: (if you use '-fpermissive', G++ will accept your code, but allowing the use of an undeclared name is deprecated)

Clang

template <int X>
struct Base
{
    void foo() { }
};

template <int X>
struct Derive : Base<X>
{
    void bar() { foo(); }
};
> clang++-3.8 -Wall -Wextra -pedantic main.cpp
main.cpp:10:18: error: use of undeclared identifier 'foo'
    void bar() { foo(); }
                 ^
main.cpp:11:3: warning: no newline at end of file [-Wnewline-eof]

Explication: Derive<X> connaîtra le contenu de Base<X> quand Base<X> sera instancié, donc quand Derive<X> sera lui-même instancié (pas lors du parsing de Derive<X>)

template <int X>
struct Base {
   void foo() { }
};

template <>
struct Base<42> {    // spécialisation de Base<X>
   void rien() { }   // pas de fonction foo()
};

template <int X>
struct Derive : Base<X> {  // Et si X=42 ?
   void bar() { foo(); }   // D'où vient foo() ?
};

Correction en utilisant this->

template <int X>
struct Base
{
    void foo() { }
};

template <int X>
struct Derive : Base<X>
{
    void bar() { this->foo(); }  // this->
};

Correction en utilisant Base<X>::

template <int X>
struct Base
{
    void foo() { }
};

template <int X>
struct Derive : Base<X>
{
    void bar() { Base<X>::foo(); }  // Base<X>::
};

Correction en utilisant using Base<X>::foo

template <int X>
struct Base
{
    void foo() { }
};

template <int X>
struct Derive : Base<X>
{
    using Base<X>::foo;    // using
    void bar() { foo(); }
};

Where foo is declared?

#include <iostream>
#include <string>

int main()
{
    std::string(foo)(1, '#');
    std::cout << foo << std::endl;
}

and xor or and_eq bitand compl http://en.cppreference.com/w/cpp/language/operator_alternative

http://madebyevan.com/obscure-cpp-features/

https://www.youtube.com/watch?v=_wzc7a3McOs

Mini-features (petards mouilles) :

truc marrant avec le return du main :).