diff --git a/README.md b/README.md index 6fd7403..8ffe208 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,7 @@ for(decl:col) { - [字面量、静态断言和成员函数说明符](./modern_C++_30/literalAssert) - [是不是应该返回对象?](./modern_C++_30/returnObj) - [编译期多态:泛型编程和模板入门](./modern_C++_30/compilerpoly) +- [SFINAE:不是错误的替换失败是怎么回事?](./modern_C++_30/SFINAE) ### 4.拓展部分 @@ -172,7 +173,7 @@ Ubuntu 18.04 CLion gcc/g++ -### 6.关于作者: +### 6.关于作者 个人公众号: diff --git a/modern_C++_30/CMakeLists.txt b/modern_C++_30/CMakeLists.txt index e821b35..96a917d 100644 --- a/modern_C++_30/CMakeLists.txt +++ b/modern_C++_30/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.14) project(Morden_C++) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) add_executable(heap RAII/heap.cpp) add_executable(stack RAII/stack.cpp) @@ -40,3 +40,25 @@ add_executable(literal literalAssert/literal.cpp) add_executable(assert literalAssert/assert.cpp) add_executable(default_delete literalAssert/default_delete.cpp) add_executable(overridefinal literalAssert/overridefinal.cpp) + + +add_executable(SFINAE "SFINAE/SFINAE.cpp") +add_executable(serialize "SFINAE/sfinae paper/serialize.cpp") +add_executable(hana "SFINAE/sfinae paper/hana.cpp") +add_executable(overload "SFINAE/sfinae paper/overload1.cpp") +add_executable(pSFINAE "SFINAE/sfinae paper/p1SFINAE.cpp") +add_executable(p1SFINAE "SFINAE/sfinae paper/p2SFINAE.cpp") +add_executable(sizeof1 "SFINAE/sfinae paper/sizeof1.cpp") +add_executable(sizeof2 "SFINAE/sfinae paper/sizeof2.cpp") +add_executable(combining "SFINAE/sfinae paper/combiningAndGenius.cpp") +add_executable(overload2 "SFINAE/sfinae paper/overload2.cpp") +add_executable(combining1 "SFINAE/sfinae paper/timeGenius.cpp") +add_executable(decltype "SFINAE/sfinae paper/decltype.cpp") +add_executable(auto "SFINAE/sfinae paper/auto.cpp") +add_executable(constexpr "SFINAE/sfinae paper/constexpr.cpp") +add_executable(blending "SFINAE/sfinae paper/blending1.cpp") +add_executable(blending2 "SFINAE/sfinae paper/blending2.cpp") +add_executable(lambda "SFINAE/sfinae paper/lambda.cpp") +add_executable(is_valid "SFINAE/sfinae paper/is_valid.cpp") +add_executable(fis_valid "SFINAE/sfinae paper/fis_valid.cpp") +add_executable(packis_valid "SFINAE/sfinae paper/packis_valid.cpp") diff --git a/modern_C++_30/SFINAE/SFINAE.cpp b/modern_C++_30/SFINAE/SFINAE.cpp new file mode 100644 index 0000000..af6ea9d --- /dev/null +++ b/modern_C++_30/SFINAE/SFINAE.cpp @@ -0,0 +1,41 @@ +// +// Created by light on 20-1-5. +// +#include + +using namespace std; + +template +class IsClassT { +private: + typedef char One; + typedef struct { + char a[2]; + } Two; + + template + static One test(int C::*); + + // Will be chosen if T is anything except a class. + template + static Two test(...); + +public: + enum { + Yes = sizeof(IsClassT::test(0)) == 1 + }; + enum { + No = !Yes + }; +}; + +struct A { +}; + +int main() { + // 0不能转换为int int::*因为int不是类,所以它不能有成员指针。 + cout << IsClassT::Yes << endl; + cout << IsClassT::Yes << endl; + + return 0; +} \ No newline at end of file diff --git a/modern_C++_30/SFINAE/sfinae paper/auto.cpp b/modern_C++_30/SFINAE/sfinae paper/auto.cpp new file mode 100644 index 0000000..252294f --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/auto.cpp @@ -0,0 +1,26 @@ +// +// Created by light on 20-1-6. +// +#include + +bool f(); + +auto test = f(); // Famous usage, auto deduced that test is a boolean, hurray! + + + +// vvv t wasn't declare at that point, it will be after as a parameter! +template +decltype(t.serialize()) g(const T &t) {} // Compilation error + +// Less famous usage: +// vvv auto delayed the return type specification! +// vvv vvv the return type is specified here and use t! +template +auto g(const T &t) -> decltype(t.serialize()) {} // No compilation error. + + +auto myFunction() // Automagically figures out that myFunction returns ints. +{ + return int(); +} \ No newline at end of file diff --git a/modern_C++_30/SFINAE/sfinae paper/blending1.cpp b/modern_C++_30/SFINAE/sfinae paper/blending1.cpp new file mode 100644 index 0000000..e1c4d8e --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/blending1.cpp @@ -0,0 +1,48 @@ +// +// Created by light on 20-1-6. +// + +#include +#include "structData.h" + + +template +struct hasSerialize { + // We test if the type has serialize using decltype and declval. + // decltype将根据逗号表达式选择最后一个类型作为返回值 + // 如果C有serialize将调用当前版本,没有serialize,则失败,此时编译时不报错,继续寻找重载函数,也就是SFINAE. + template + static constexpr decltype(std::declval().serialize(), bool()) test(int /* unused */) { + // We can return values, thanks to constexpr instead of playing with sizeof. + return true; + } + + template + static constexpr bool test(...) { + return false; + } + + // int is used to give the precedence! + static constexpr bool value = test(int()); +}; + +template +std::string serialize(const T &obj) { + // 不加constexpr 报错:error: no member named 'serialize' in 'A'. + if constexpr (hasSerialize::value) + return obj.serialize(); + else + return to_string(obj); +} + +int main() { + A a; + B b; + C c; + + // The following lines work like a charm! + std::cout << serialize(a) << std::endl; + std::cout << serialize(b) << std::endl; + std::cout << serialize(c) << std::endl; + +} \ No newline at end of file diff --git a/modern_C++_30/SFINAE/sfinae paper/blending2.cpp b/modern_C++_30/SFINAE/sfinae paper/blending2.cpp new file mode 100644 index 0000000..69f0c7e --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/blending2.cpp @@ -0,0 +1,37 @@ +// +// Created by light on 20-1-6. +// + +#include +#include "structData.h" + + +template +struct hasSerialize : std::false_type { + +}; +template +struct hasSerialize().serialize())> : std::true_type { + +}; + +template +std::string serialize(const T &obj) { + // 不加constexpr 报错:error: no member named 'serialize' in 'A'. + if constexpr (hasSerialize::value) // c++17特性 + return obj.serialize(); + else + return to_string(obj); +} + +int main() { + A a; + B b; + C c; + + // The following lines work like a charm! + std::cout << serialize(a) << std::endl; + std::cout << serialize(b) << std::endl; + std::cout << serialize(c) << std::endl; + +} \ No newline at end of file diff --git a/modern_C++_30/SFINAE/sfinae paper/combiningAndGenius.cpp b/modern_C++_30/SFINAE/sfinae paper/combiningAndGenius.cpp new file mode 100644 index 0000000..0f8dc2d --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/combiningAndGenius.cpp @@ -0,0 +1,96 @@ +// +// Created by light on 20-1-6. +// + +#include +#include "structData.h" + + +template +struct hasSerialize { + // 编译时比较 + typedef char yes[1]; + typedef char no[2]; + // 允许我们检查序列化确实是一种方法 + // 第二个参数必须是第一个参数的类型 + // 例如: reallyHas 替换为 reallyHas 并起作用 + // 注意:它仅适用于整数常量和指针(因此函数指针可以使用) + // 例如:reallyHas 替换为 + // reallyHas 并起作用 + template + struct reallyHas; + + // std::string (C::*)() 是函数指针声明 + template + static yes &test(reallyHas * /*unused*/) {} + + // ()()const 函数指针 -> std::string serialize() const + template + static yes &test(reallyHas * /*unused*/) {} + + // The famous C++ sink-hole. + // Note that sink-hole must be templated too as we are testing test(0). + // If the method serialize isn't available, we will end up in this method. + template + static no &test(...) { /* dark matter */ } + + //用作测试的返回值的常数。 + //由于编译时评估的大小,因此实际上在这里完成了测试。 + static const bool value = sizeof(test(0)) == sizeof(yes); +}; + +// Using the previous A struct and hasSerialize helper. +struct D : A { + std::string serialize() const { + return "I am a D!"; + } +}; + +template +bool testHasSerialize(const T & /*t*/) { return hasSerialize::value; } + +// 不能够判断仿函数里面的serialize +struct E { + struct Functor { + std::string operator()() { + return "I am a E!"; + } + }; + + Functor serialize; +}; + +template +std::string serialize(const T &obj) { + // 不加constexpr 报错:error: no member named 'serialize' in 'A'. + if constexpr (hasSerialize::value) + return obj.serialize(); + else + return to_string(obj); +} + +int main() { + // 检测结构体是否有serialize方法 + // Using the struct A, B, C defined in the previous hasSerialize example. + std::cout << hasSerialize::value << std::endl; + std::cout << hasSerialize::value << std::endl; + std::cout << hasSerialize::value << std::endl; + + + D d; + A &a = d; // Here we lost the type of d at compile time. + std::cout << testHasSerialize(d) << std::endl; // Output 1. + std::cout << testHasSerialize(a) << std::endl; // Output 0. + + E e; + std::cout << e.serialize() << std::endl; // Succefully call the functor. + std::cout << testHasSerialize(e) << std::endl; // Output 0. + + + A a_; + B b_; + C c_; + std::cout << serialize(a_) << std::endl; + std::cout << serialize(b_) << std::endl; + std::cout << serialize(c_) << std::endl; +} \ No newline at end of file diff --git a/modern_C++_30/SFINAE/sfinae paper/constexpr.cpp b/modern_C++_30/SFINAE/sfinae paper/constexpr.cpp new file mode 100644 index 0000000..af56b85 --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/constexpr.cpp @@ -0,0 +1,25 @@ +// +// Created by light on 20-1-6. +// +#include + +constexpr int factorial(int n) { + return n <= 1 ? 1 : (n * factorial(n - 1)); +} + +struct testStruct : std::true_type { +}; // Inherit from the true type. + +int main() { + int i = factorial(5); // Call to a constexpr function. + // Will be replace by a good compiler by: + // int i = 120; + std::cout << i << std::endl; + + + constexpr bool testVar = testStruct(); // Generate a compile-time testStruct. + bool test = testStruct::value; // Equivalent to: test = true; + std::cout << test << std::endl; + test = testVar; // true_type has a constexpr converter operator, equivalent to: test = true; + std::cout << test << std::endl; +} diff --git a/modern_C++_30/SFINAE/sfinae paper/decltype.cpp b/modern_C++_30/SFINAE/sfinae paper/decltype.cpp new file mode 100644 index 0000000..56e2a00 --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/decltype.cpp @@ -0,0 +1,20 @@ +// +// Created by light on 20-1-6. +// +#include +struct Default { + int foo() const {return 1;} +}; + +struct NonDefault { + NonDefault(const NonDefault&) {} + int foo() const {return 1;} +}; + +int main() +{ + decltype(Default().foo()) n1 = 1; // int n1 +// decltype(NonDefault().foo()) n2 = n1; // error: no default constructor + decltype(std::declval().foo()) n2 = n1; // int n2 + std::cout << "n2 = " << n2 << '\n'; +} \ No newline at end of file diff --git a/modern_C++_30/SFINAE/sfinae paper/fis_valid.cpp b/modern_C++_30/SFINAE/sfinae paper/fis_valid.cpp new file mode 100644 index 0000000..dd5db43 --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/fis_valid.cpp @@ -0,0 +1,78 @@ +// +// Created by light on 20-1-6. +// + +#include +#include "boost/hana.hpp" +#include "structData.h" + +template +struct container { +// Let's put the test in private. +private: + // We use std::declval to 'recreate' an object of 'UnnamedType'. + // We use std::declval to also 'recreate' an object of type 'Param'. + // We can use both of these recreated objects to test the validity! + template + constexpr auto testValidity(int /* unused */) + -> decltype(std::declval()(std::declval()),boost::hana::true_c) { + // If substitution didn't fail, we can return a true_type. + return boost::hana::true_c; + } + + template + constexpr std::false_type testValidity(...) { + // Our sink-hole returns a false_type. + return boost::hana::false_c; + } + +public: + // A public operator() that accept the argument we wish to test onto the UnnamedType. + // Notice that the return type is automatic! + template + constexpr auto operator()(const Param &p) { + // The argument is forwarded to one of the two overloads. + // The SFINAE on the 'true_type' will come into play to dispatch. + // Once again, we use the int for the precedence. + return testValidity(int()); + } +}; + +template +constexpr auto is_valid(const UnnamedType &t) { + // We used auto for the return type: it will be deduced here. + return container(); +} + +// Check if a type has a serialize method. +auto hasSerialize = is_valid([](auto &&x) -> decltype(x.serialize()) {}); + +// Notice how I simply swapped the return type on the right? +template +auto serialize(T &obj) +-> typename std::enable_if::type { + return obj.serialize(); +} + +template +auto serialize(T &obj) +-> typename std::enable_if::type { + return to_string(obj); +} + +int main() { + A a; + B b; + C c; + auto test = is_valid([](const auto &t) -> decltype(t.serialize()) {}); + + std::cout << test(a) << std::endl; + std::cout << test(b) << std::endl; + std::cout << test(c) << std::endl; + + + // The following lines work like a charm! + std::cout << serialize(a) << std::endl; + std::cout << serialize(b) << std::endl; + std::cout << serialize(c) << std::endl; +} diff --git a/modern_C++_30/SFINAE/sfinae paper/hana.cpp b/modern_C++_30/SFINAE/sfinae paper/hana.cpp new file mode 100644 index 0000000..8565428 --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/hana.cpp @@ -0,0 +1,28 @@ +// +// Created by light on 20-1-6. +// +// https://www.boost.org/doc/libs/1_62_0/libs/hana/doc/html/structboost_1_1hana_1_1type.html#a2d2e7e08e284f7e0bd1bd9c3ad0e0a2b +#include +#include +#include + +namespace hana = boost::hana; + + +int main() { + // 检查成员 + struct Person { + std::string name; + }; + auto has_name = hana::is_valid([](auto &&p) -> decltype(p.name) {}); + + Person joe{"Joe"}; + static_assert(has_name(joe), ""); + static_assert(!has_name(1), ""); + // 检查嵌套类型 + auto has_value_type = hana::is_valid([](auto t) -> hana::type {}); + static_assert(has_value_type(hana::type_c>), ""); + static_assert(!has_value_type(hana::type_c), ""); + + return 0; +} \ No newline at end of file diff --git a/modern_C++_30/SFINAE/sfinae paper/is_valid.cpp b/modern_C++_30/SFINAE/sfinae paper/is_valid.cpp new file mode 100644 index 0000000..d051ba0 --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/is_valid.cpp @@ -0,0 +1,78 @@ +// +// Created by light on 20-1-6. +// + +#include +#include "structData.h" + +template +struct container { +// Let's put the test in private. +private: + // We use std::declval to 'recreate' an object of 'UnnamedType'. + // We use std::declval to also 'recreate' an object of type 'Param'. + // We can use both of these recreated objects to test the validity! + template + constexpr auto testValidity(int /* unused */) + -> decltype(std::declval()(std::declval()), std::true_type()) { + // If substitution didn't fail, we can return a true_type. + return std::true_type(); + } + + template + constexpr std::false_type testValidity(...) { + // Our sink-hole returns a false_type. + return std::false_type(); + } + +public: + // A public operator() that accept the argument we wish to test onto the UnnamedType. + // Notice that the return type is automatic! + template + constexpr auto operator()(const Param &p) { + // The argument is forwarded to one of the two overloads. + // The SFINAE on the 'true_type' will come into play to dispatch. + // Once again, we use the int for the precedence. + return testValidity(int()); + } +}; + +template +constexpr auto is_valid(const UnnamedType &t) { + // We used auto for the return type: it will be deduced here. + return container(); +} + +// Check if a type has a serialize method. +auto hasSerialize = is_valid([](auto &&x) -> decltype(x.serialize()) {}); + +// Notice how I simply swapped the return type on the right? +template +auto serialize(T &obj) +-> typename std::enable_if::type { + return obj.serialize(); +} + +template +auto serialize(T &obj) +-> typename std::enable_if::type { + return to_string(obj); +} + +int main() { + A a; + B b; + C c; + auto test = is_valid([](const auto &t) -> decltype(t.serialize()) {}); + + std::cout << test(a) << std::endl; + std::cout << test(b) << std::endl; + std::cout << test(c) << std::endl; + + + // The following lines work like a charm! + std::cout << serialize(a) << std::endl; + std::cout << serialize(b) << std::endl; + std::cout << serialize(c) << std::endl; + +} diff --git a/modern_C++_30/SFINAE/sfinae paper/lambda.cpp b/modern_C++_30/SFINAE/sfinae paper/lambda.cpp new file mode 100644 index 0000000..12c4553 --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/lambda.cpp @@ -0,0 +1,62 @@ +// +// Created by light on 20-1-6. +// +#include +#include "structData.h" +// Equivalent to: +struct l5UnamedType { + template + auto operator()(T &t) const -> decltype(t.serialize()) // /!\ This signature is nice for a SFINAE! + { + return t.serialize(); + } +}; + + +void fun(A a,B b,C c) { + + // ***** Simple lambda unamed type ***** + auto l4 = [](int a, int b) { return a + b; }; + std::cout << l4(4, 5) << std::endl; // Output 9. + + // Equivalent to: + struct l4UnamedType { + int operator()(int a, int b) const { + return a + b; + } + }; + + l4UnamedType l4Equivalent = l4UnamedType(); + std::cout << l4Equivalent(4, 5) << std::endl; // Output 9 too. + + + + // ***** auto parameters lambda unnamed type ***** + + // b's type is automagically deduced! + auto l5 = [](auto &t) -> decltype(t.serialize()) { return t.serialize(); }; + + std::cout << l5(b) << std::endl; // Output: I am a B! +// std::cout << l5(a) << std::endl; // Error: no member named 'serialize' in 'A'. + + + l5UnamedType l5Equivalent = l5UnamedType(); + + std::cout << l5Equivalent(b) << std::endl; // Output: I am a B! +// std::cout << l5Equivalent(a) << std::endl; // Error: no member named 'serialize' in 'A'. + +} +int main() { + A a; + B b; + C c; + auto l1 = [](B &b) { return b.serialize(); }; // Return type figured-out by the return statement. + auto l3 = [](B &b) -> std::string { return b.serialize(); }; // Fixed return type. + auto l2 = [](B &b) -> decltype(b.serialize()) { return b.serialize(); }; // Return type dependant to the B type. + + std::cout << l1(b) << std::endl; // Output: I am a B! + std::cout << l2(b) << std::endl; // Output: I am a B! + std::cout << l3(b) << std::endl; // Output: I am a B! + + fun(a,b,c); +} \ No newline at end of file diff --git a/modern_C++_30/SFINAE/sfinae paper/overload1.cpp b/modern_C++_30/SFINAE/sfinae paper/overload1.cpp new file mode 100644 index 0000000..a87ddf3 --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/overload1.cpp @@ -0,0 +1,14 @@ +// +// Created by light on 20-1-6. +// +#include + +void f(std::string s); // int can't be convert into a string. +void f(double d); // int can be implicitly convert into a double, so this version could be selected, but... +void f(int i); // ... this version using the type int directly is even more close! + + + +int main() { + f(1); // Call f(int i); +} diff --git a/modern_C++_30/SFINAE/sfinae paper/overload2.cpp b/modern_C++_30/SFINAE/sfinae paper/overload2.cpp new file mode 100644 index 0000000..45b62ec --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/overload2.cpp @@ -0,0 +1,20 @@ +// +// Created by light on 20-1-6. +// +#include + +std::string f(...) // Variadic functions are so "untyped" that... +{ + return "..."; +} + +template +std::string f(const T &t)// ...this templated function got the precedence! +{ + + return "T"; +} + +int main() { + std::cout << f(1) << std::endl; +} \ No newline at end of file diff --git a/modern_C++_30/SFINAE/sfinae paper/p1SFINAE.cpp b/modern_C++_30/SFINAE/sfinae paper/p1SFINAE.cpp new file mode 100644 index 0000000..6e0db20 --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/p1SFINAE.cpp @@ -0,0 +1,23 @@ +// +// Created by light on 20-1-6. +// + +/* + The compiler will try this overload since it's less generic than the variadic. + T will be replace by int which gives us void f(const int& t, int::iterator* b = nullptr); + int doesn't have an iterator sub-type, but the compiler doesn't throw a bunch of errors. + It simply tries the next overload. + + 编译器尝试此重载,因为模板化函数比可变参数函数更精确(通用)。T将被int取代,这将使我们得到 + void f(const int& t, int::iterator* b = nullptr); int 没有迭代器子类型,但是编译器不会抛出一堆错误。 + 它只是尝试下一个重载。 +*/ +template +void f(const T &t, typename T::iterator *it = nullptr) {} + +// The sink-hole. +void f(...) {} + +int main() { + f(1); // Calls void f(...) { } +} diff --git a/modern_C++_30/SFINAE/sfinae paper/p2SFINAE.cpp b/modern_C++_30/SFINAE/sfinae paper/p2SFINAE.cpp new file mode 100644 index 0000000..115e813 --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/p2SFINAE.cpp @@ -0,0 +1,13 @@ +// +// Created by light on 20-1-6. +// + + +// The compiler will be really unhappy when it will later discover the call to hahahaICrash. +// 当以后发现对hahahaICrash的调用时,编译器将非常不满意。 +template void f(T t) { t.hahahaICrash(); } +void f(...) { } // The sink-hole wasn't even considered. + +int main() { + f(1); +} \ No newline at end of file diff --git a/modern_C++_30/SFINAE/sfinae paper/packis_valid.cpp b/modern_C++_30/SFINAE/sfinae paper/packis_valid.cpp new file mode 100644 index 0000000..7fe7b4d --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/packis_valid.cpp @@ -0,0 +1,77 @@ +// +// Created by light on 20-1-6. +// + +#include +#include "structData.h" + +template struct container +{ +// Let's put the test in private. +private: + // We use std::declval to 'recreate' an object of 'UnnamedType'. + // We use std::declval to also 'recreate' an object of type 'Param'. + // We can use both of these recreated objects to test the validity! + template constexpr auto test_validity(int /* unused */) + -> decltype(std::declval()(std::declval()...), std::true_type()) + { + // If substitution didn't fail, we can return a true_type. + return std::true_type(); + } + + template constexpr std::false_type test_validity(...) + { + // Our sink-hole returns a false_type. + return std::false_type(); + } + +public: + // A public operator() that accept the argument we wish to test onto the UnnamedType. + // Notice that the return type is automatic! + template constexpr auto operator()(Params&& ...) + { + // The argument is forwarded to one of the two overloads. + // The SFINAE on the 'true_type' will come into play to dispatch. + return test_validity(int()); + } +}; + +template constexpr auto is_valid(UnnamedType&& t) +{ + // We used auto for the return type: it will be deduced here. + return container(); +} + +// Check if a type has a serialize method. +auto hasSerialize = is_valid([](auto &&x) -> decltype(x.serialize()) {}); + +// Notice how I simply swapped the return type on the right? +template +auto serialize(T &obj) +-> typename std::enable_if::type { + return obj.serialize(); +} + +template +auto serialize(T &obj) +-> typename std::enable_if::type { + return to_string(obj); +} + +int main() { + A a; + B b; + C c; + auto test = is_valid([](const auto &t) -> decltype(t.serialize()) {}); + + std::cout << test(a,b) << std::endl; + std::cout << test(b) << std::endl; + std::cout << test(c) << std::endl; + + + // The following lines work like a charm! + std::cout << serialize(a) << std::endl; + std::cout << serialize(b) << std::endl; + std::cout << serialize(c) << std::endl; + +} diff --git a/modern_C++_30/SFINAE/sfinae paper/serialize.cpp b/modern_C++_30/SFINAE/sfinae paper/serialize.cpp new file mode 100644 index 0000000..a6b9ec9 --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/serialize.cpp @@ -0,0 +1,35 @@ +// +// Created by light on 20-1-6. +// + +#include +#include +#include +#include "structData.h" +using namespace std; + +namespace hana = boost::hana; +// 检查类型是否有一个serialize方法 +auto hasSerialize = hana::is_valid([](auto &&x) -> decltype(x.serialize()) {}); + +// 序列化任意对象 +template +std::string serialize(T const &obj) { + return hana::if_(hasSerialize(obj), + [](auto &x) { return x.serialize(); }, + [](auto &x) { return to_string(x); } + )(obj); +} + + + +int main() { + A a; + B b; + C c; + std::cout << serialize(a) << std::endl; + std::cout << serialize(b) << std::endl; + std::cout << serialize(c) << std::endl; +} + + diff --git a/modern_C++_30/SFINAE/sfinae paper/sizeof1.cpp b/modern_C++_30/SFINAE/sfinae paper/sizeof1.cpp new file mode 100644 index 0000000..821a5ea --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/sizeof1.cpp @@ -0,0 +1,17 @@ +// +// Created by light on 20-1-6. +// + +#include + +typedef char type_test[42]; + +type_test &f() {} + +int main() { + + // In the following lines f won't even be truly called but we can still access to the size of its return type. + // Thanks to the "fake evaluation" of the sizeof operator. + char arrayTest[sizeof(f())]; + std::cout << sizeof(f()) << std::endl; // Output 42. +} \ No newline at end of file diff --git a/modern_C++_30/SFINAE/sfinae paper/sizeof2.cpp b/modern_C++_30/SFINAE/sfinae paper/sizeof2.cpp new file mode 100644 index 0000000..a0a202a --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/sizeof2.cpp @@ -0,0 +1,18 @@ +#include + +// +// Created by light on 20-1-6. +// + +typedef char yes; // Size: 1 byte. +typedef yes no[2]; // Size: 2 bytes. + +// Two functions using our type with different size. +yes &f1() {} +no &f2() {} + +int main() { + std::cout << sizeof(f1()) << std::endl; + std::cout << sizeof(f2()) << std::endl; + std::cout << (sizeof(f1()) == sizeof(f2())) << std::endl; // Output 0. +} \ No newline at end of file diff --git a/modern_C++_30/SFINAE/sfinae paper/structData.h b/modern_C++_30/SFINAE/sfinae paper/structData.h new file mode 100644 index 0000000..38ff943 --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/structData.h @@ -0,0 +1,30 @@ +// +// Created by light on 20-1-6. +// + +#ifndef MORDEN_C_STRUCTDATA_H +#define MORDEN_C_STRUCTDATA_H +// 类型A只有to_string 方法 +struct A { +}; + +std::string to_string(const A &) { + return "I am A"; +} + +// 类型B有serialize方法 +struct B { + std::string serialize() const { + return "I am B"; + } +}; + +// 类型C有个serialize数据成员与to_string方法 +struct C { + std::string serialize; +}; + +std::string to_string(const C &) { + return "I am C"; +} +#endif //MORDEN_C_STRUCTDATA_H diff --git a/modern_C++_30/SFINAE/sfinae paper/timeGenius.cpp b/modern_C++_30/SFINAE/sfinae paper/timeGenius.cpp new file mode 100644 index 0000000..0d69e76 --- /dev/null +++ b/modern_C++_30/SFINAE/sfinae paper/timeGenius.cpp @@ -0,0 +1,81 @@ +// +// Created by light on 20-1-6. +// + +#include +#include "structData.h" + + +template +struct hasSerialize { + // 编译时比较 + typedef char yes[1]; + typedef char no[2]; + // 允许我们检查序列化确实是一种方法 + // 第二个参数必须是第一个参数的类型 + // 例如: reallyHas 替换为 reallyHas 并起作用 + // 注意:它仅适用于整数常量和指针(因此函数指针可以使用) + // 例如:reallyHas 替换为 + // reallyHas 并起作用 + template + struct reallyHas; + + // std::string (C::*)() 是函数指针声明 + template + static yes &test(reallyHas * /*unused*/) {} + + // ()()const 函数指针 -> std::string serialize() const + template + static yes &test(reallyHas * /*unused*/) {} + + // The famous C++ sink-hole. + // Note that sink-hole must be templated too as we are testing test(0). + // If the method serialize isn't available, we will end up in this method. + template + static no &test(...) { /* dark matter */ } + + //用作测试的返回值的常数。 + //由于编译时评估的大小,因此实际上在这里完成了测试。 + static const bool value = sizeof(test(0)) == sizeof(yes); +}; + + +template // Default template version. +struct enable_if { +}; // This struct doesn't define "type" and the substitution will fail if you try to access it. + +template // A specialisation used if the expression is true. +struct enable_if { + typedef T type; +}; // This struct do have a "type" and won't fail on access. + +template +typename enable_if::value, std::string>::type serialize(const T &obj) { + return obj.serialize(); +} + +template +typename enable_if::value, std::string>::type serialize(const T &obj) { + return to_string(obj); +} + +int main() { + + // Usage: +// enable_if::type t1; // Compiler happy. t's type is int. +// enable_if::value, int>::type t2; // Compiler happy. t's type is int. +// +// enable_if::type t3; // Compiler unhappy. no type named 'type' in 'enable_if'; +// enable_if::value, int>::type t4; // no type named 'type' in 'enable_if'; + + + A a; + B b; + C c; + + // The following lines work like a charm! + std::cout << serialize(a) << std::endl; + std::cout << serialize(b) << std::endl; + std::cout << serialize(c) << std::endl; + +} \ No newline at end of file