cppmap / cppmap.docs

🗺️ Source code of "C++ の歩き方": C++20/23/26 resources written in Japanese.
https://cppmap.github.io/
The Unlicense
179 stars 20 forks source link

C++ の面白いコード、トリッキーなコードの話 #9

Open Reputeless opened 5 years ago

Reputeless commented 5 years ago

実用性はなくてもいいので、面白いネタ的なコードや不思議な文法、仕様のハックなどがあれば教えてください。ある程度たまったら記事にします。

(例) コメントアウトのトリック

faithandbrave commented 5 years ago

よくある、ヘッダファイル以外をインクルードするとかは、データをコンパイル時に埋め込める便利な記法です。

int ar[] = {
    #include "a.csv"
};

ヘッダファイル内で.cpp (相当のファイル) をインクルードするオプションを用意して、ヘッダオンリー化できるようにしているのとかは、ライブラリを作るにあたって設計選択のひとつになっています。

https://github.com/boostorg/chrono/blob/develop/include/boost/chrono/thread_clock.hpp

(最近はシングルヘッダ版を自動生成するとかもありますが)

faithandbrave commented 5 years ago

プリプロセス関係だと、文字列リテラルの結合とかも、マイナーですがたまに便利ですね。自前のprintfやassertを作るときにも使いますし、長い文字列リテラルを分割して書くのにも便利です。

#include <cstdio>

#define INFO(fmt, ...) std::printf("[info] " fmt "\n" __VA_OPT__(,) __VA_ARGS__)
#define DEBUG(fmt, ...) std::printf("[debug] " fmt "\n" __VA_OPT__(,) __VA_ARGS__)

int main()
{
    INFO("hello");
    DEBUG("hello %d", 123);
}
#include <iostream>

int main()
{
    std::string help = "The application is designed by ME.\n"
                       "Welcome my application";

    std::cout << help << std::endl;
}
faithandbrave commented 5 years ago

csvのインクルードとかを多用するようになると、以前私がブログに書いた以下のようなものがほしくなります。

Reputeless commented 5 years ago

コメントありがとうございます! 引き続き募集してます。

--> 演算子

#include <iostream>

int main()
{
    int i = 10;

    while (i --> 0)
    {
        std::cout << i << '\n';
    }
}
wx257osn2 commented 5 years ago

template関数のオーバーロードに優先順位を付けるやつ:

#include<cstdint>
template<std::size_t N>struct priority : priority<N-1>{};
template<>             struct priority<0>{};

#include<iostream>
#include<type_traits>
template<
  typename T,
  std::enable_if_t<std::is_same<std::decay_t<T>, float>::value, std::nullptr_t> = nullptr
>
void f_impl(T t, priority<1>){std::cout << "float " << t << std::endl;}
template<typename T>
void f_impl(T t, priority<0>){std::cout << "T " << t << std::endl;}

template<typename T>
void f(T&& t){f_impl(std::forward<T>(t), priority<1>{});}

int main(){
  f(3.1);
  f(3.14f);
  f("3.141592");
}

Wandbox

onihusube commented 5 years ago

面白いかは分からないですが、リテラル0だけを受け取る関数(nullptrからは目を逸らして・・・)

struct minus {
  constexpr bool operator<(std::nullptr_t) {
    return true;
  }

  constexpr bool operator>(std::nullptr_t) {
    return false;
  }
};

void f(std::nullptr_t) {
  std::cout << "literal 0" << std::endl;
}

https://wandbox.org/permlink/sDe2GzeQuPk1hMT0

Fuyutsubaki commented 5 years ago

コンパイルできそうだけどできないコードたち

void f2(std::shared_ptr<int>=0);
void f(int*=0);
int a[3];
a[[]{return 0;}()];

https://wandbox.org/permlink/P76EMNyoE6FAQaDs

yumetodo commented 5 years ago

false_vを作らないでlambda式でif constexpr内のstatic_assertの評価を遅延させるやつ

#include <type_traits>

template <typename T>
void f(T)
{
  if constexpr (std::is_same_v<T, int>)
  {
    // Tがintのときのみ評価される
    static_assert([]{return false;}());
  }
}

int main()
{
  f(2.4);
  f(3);
}

https://cpprefjp.github.io/lang/cpp17/if_constexpr.html https://github.com/cpprefjp/site/pull/577 by @alphya

yumetodo commented 5 years ago

input streamの中身をまるごとoutput streamにぶちこむやつ

os << is.rdbuf();
rhysd commented 5 years ago

Most vexing parse: https://en.wikipedia.org/wiki/Most_vexing_parse

agehama commented 5 years ago

暗黙の型変換にやられるやつ(ただのバグ)

#include <iostream>

int main()
{
    if(-1 < 1u)
    {
        std::cout << "-1 < 1u";
    }
    else
    {
        std::cout << "-1 > 1u";
    }
}
agehama commented 5 years ago

名前空間の付け忘れにやられるやつ(ただのバグ)

#include <iostream>
#include <cmath>

int main()
{
    //std::cout << std::abs(0.5); // 0.5
    std::cout << abs(0.5);        // 0
}
nekko1119 commented 5 years ago
template <int>
int a = 0;

int main() {
    //a<10>= 1; // 動かない
    a<10> = 1;
}

スペースの有無で演算子と解釈されるかどうか異なります

Fuyutsubaki commented 5 years ago

(特別C++に限らないけけど) Fast inverse square root とか

zxc-key commented 4 years ago

https://wandbox.org/permlink/fXQKYqQAgMiyMMf9

yumetodo commented 4 years ago

constexprが普通に使えるこんにちは出番がなさそう・・・?

Reputeless commented 4 years ago

@zxc-key 提案ありがとうございます。

  1. enum をコンパイル時定数として扱うのは今では古いテクニック (現在は constexpr) で、ある程度知られているとも思います
  2. 「最後尾 = 要素数」は、サンプルコードでいうと _0 = 100 のようなコードで不正になります。magic_enum というライブラリの enum_count では、複雑なテクニックでその問題も回避しています
  3. cppmap 掲載済みです https://cppmap.github.io/articles/it-compiles/#_2

【面白いネタ的なコードや不思議な文法】として cppmap で新規に取り上げるネタはありませんでしたが、また面白いコードを見つけたら教えてください 🖐

yohhoy commented 3 years ago
int main()
{
  []{};
  []{}();
  [](){};
  [](){}();
  [[]][]{};
  []()[[]]{};
  [[]][]{}();
  [[]][](){};
  []()[[]]{}();
  [[]][](){}();
  [[]][]()[[]]{};
  [[]][]()[[]]{}();
}

ネタの一つとして提供:https://twitter.com/yohhoy/status/1367790416007897089

yaito3014 commented 3 years ago
#include <iostream>

using T = int&;
void f(T&) { std::cout << "non-const" << std::endl; }
void f(const T&) { std::cout << "const" << std::endl; }

int main() {
  int x;
  f(x);
}

コンパイルできそうでできないやつです。

yumetodo commented 3 years ago
#include <type_traits>
using T = int&;
using T2 = const T&;
static_assert(std::is_same_v<T, T2>);

なるほど・・・? https://wandbox.org/permlink/SVngPcUtKYXAL72O

Ryoga-exe commented 3 years ago

長い演算子 (演算子の優先順位的にできるやつ)

#include <iostream>

int main() {
  int a = 10, b = 10;
  a = a +-+-+-+-+-+-+-+-+ 6 +-+-+-+-+-+-+-+-+ 5;
  b = b + + + + + + + + + 6 - - - - - - - - - 5;
  std::cout << a << '\n';
  std::cout << b << '\n';
  return 0;
}