c++ définition - Moyen le plus simple de déterminer le type de fonction de retour



php cours (4)

Je ne sais pas si la manière la plus simple (si vous pouvez utiliser C ++ 17 n'est certainement pas: voir la réponse de NathanOliver) mais ... qu'en est-il de la déclaration d'une fonction comme suit:

template <typename R, typename ... Args>
R getRetType (R(*)(Args...));

et en utilisant decltype() ?

using ReturnTypeOfFoo = decltype( getRetType(&foo) );

Observez que getRetType() est uniquement déclaré et non défini car appelé uniquement decltype() , seul le type renvoyé est pertinent.

Étant donné une fonction très simple mais longue, telle que:

int foo(int a, int b, int c, int d) {
    return 1;
}

// using ReturnTypeOfFoo = ???

Quel est le moyen le plus simple et le plus concis de déterminer le type de retour de la fonction ( ReturnTypeOfFoo , dans cet exemple: int ) au moment de la compilation sans répéter les types de paramètre de la fonction (par nom uniquement, car il est connu que la fonction ne comporte pas de surcharge supplémentaire )?


Vous pouvez utiliser std::function ici, ce qui vous donnera un typedef pour le type de retour des fonctions. Cela nécessite la prise en charge de C ++ 17, car il repose sur la déduction d'argument de modèle de classe , mais cela fonctionnera avec n'importe quel type appelable:

using ReturnTypeOfFoo = decltype(std::function{foo})::result_type;

Nous pouvons rendre cela un peu plus générique comme

template<typename Callable>
using return_type_of_t = 
    typename decltype(std::function{std::declval<Callable>()})::result_type;

qui vous permet ensuite de l'utiliser comme

int foo(int a, int b, int c, int d) {
    return 1;
}

auto bar = [](){ return 1; };

struct baz_ 
{ 
    double operator()(){ return 0; } 
} baz;

using ReturnTypeOfFoo = return_type_of_t<decltype(foo)>;
using ReturnTypeOfBar = return_type_of_t<decltype(bar)>;
using ReturnTypeOfBaz = return_type_of_t<decltype(baz)>;

Le plus simple et concis est probablement:

template <typename R, typename... Args>
R return_type_of(R(*)(Args...));

using ReturnTypeOfFoo = decltype(return_type_of(foo));

Notez que cela ne fonctionnera pas pour les objets de fonction ou les pointeurs vers des fonctions membres. Juste des fonctions, qui ne sont pas surchargées ni des modèles, ou noexcept .

Mais cela peut être étendu pour prendre en charge tous ces cas, si vous le souhaitez, en ajoutant davantage de surcharges de return_type_of .


TL; DR: Utilisez __builtin intrinsics à la place.

J'ai réussi à faire gcc 4.8.4 (et même 4.7.3 sur gcc.godbolt.org) générer un code optimal pour cela en utilisant __builtin_popcountll qui utilise la même instruction d'assemblage, mais qui n'a pas ce bug de fausse dépendance.

Je ne suis pas sûr à 100% de mon code de benchmarking, mais la sortie d' objdump semble partager mon point de vue. J'utilise d'autres astuces ( ++i vs i++ ) pour que le compilateur déroule la boucle sans aucune instruction movl (comportement étrange, je dois dire).

Résultats:

Count: 20318230000  Elapsed: 0.411156 seconds   Speed: 25.503118 GB/s

Code d'analyse comparative:

#include <stdint.h>
#include <stddef.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>

uint64_t builtin_popcnt(const uint64_t* buf, size_t len){
  uint64_t cnt = 0;
  for(size_t i = 0; i < len; ++i){
    cnt += __builtin_popcountll(buf[i]);
  }
  return cnt;
}

int main(int argc, char** argv){
  if(argc != 2){
    printf("Usage: %s <buffer size in MB>\n", argv[0]);
    return -1;
  }
  uint64_t size = atol(argv[1]) << 20;
  uint64_t* buffer = (uint64_t*)malloc((size/8)*sizeof(*buffer));

  // Spoil copy-on-write memory allocation on *nix
  for (size_t i = 0; i < (size / 8); i++) {
    buffer[i] = random();
  }
  uint64_t count = 0;
  clock_t tic = clock();
  for(size_t i = 0; i < 10000; ++i){
    count += builtin_popcnt(buffer, size/8);
  }
  clock_t toc = clock();
  printf("Count: %lu\tElapsed: %f seconds\tSpeed: %f GB/s\n", count, (double)(toc - tic) / CLOCKS_PER_SEC, ((10000.0*size)/(((double)(toc - tic)*1e+9) / CLOCKS_PER_SEC)));
  return 0;
}

Compiler les options:

gcc --std=gnu99 -mpopcnt -O3 -funroll-loops -march=native bench.c -o bench

Version GCC:

gcc (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4

Version du noyau Linux:

3.19.0-58-generic

Informations sur le processeur:

processor   : 0
vendor_id   : GenuineIntel
cpu family  : 6
model       : 70
model name  : Intel(R) Core(TM) i7-4870HQ CPU @ 2.50 GHz
stepping    : 1
microcode   : 0xf
cpu MHz     : 2494.226
cache size  : 6144 KB
physical id : 0
siblings    : 1
core id     : 0
cpu cores   : 1
apicid      : 0
initial apicid  : 0
fpu     : yes
fpu_exception   : yes
cpuid level : 13
wp      : yes
flags       : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx rdtscp lm constant_tsc nopl xtopology nonstop_tsc eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm arat pln pts dtherm fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 invpcid xsaveopt
bugs        :
bogomips    : 4988.45
clflush size    : 64
cache_alignment : 64
address sizes   : 36 bits physical, 48 bits virtual
power management:




c++ function c++17 return-type compile-time